Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# Copyright: (c) 2026, Matt Tarkington (@mtarking)

# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
ND Manage Fabric Group Members endpoint models.

This module contains endpoint definitions for fabric group member operations
in the ND Manage API.

## Endpoints

- `EpManageFabricGroupMembersGet` - List members of a fabric group
(GET /api/v1/manage/fabrics/{fabric_name}/members)
- `EpManageFabricGroupMembersAddPost` - Add members to a fabric group
(POST /api/v1/manage/fabrics/{fabric_name}/actions/addMembers)
- `EpManageFabricGroupMembersRemovePost` - Remove members from a fabric group
(POST /api/v1/manage/fabrics/{fabric_name}/actions/removeMembers)
"""

from __future__ import annotations

__metaclass__ = type

from typing import ClassVar, Literal

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 Field
from ansible_collections.cisco.nd.plugins.module_utils.types import IdentifierKey


class _EpManageFabricGroupMembersBase(FabricNameMixin, NDEndpointBaseModel):
"""
Base class for ND Manage Fabric Group Members endpoints.

Provides common functionality for all HTTP methods on the
/api/v1/manage/fabrics/{fabric_name}/members and
/api/v1/manage/fabrics/{fabric_name}/actions/addMembers|removeMembers endpoints.

Subclasses override ``_path_suffix`` to build the correct path.
"""

_path_suffix: ClassVar[str] = "members"

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:
"""
Build the endpoint path including fabric name and path suffix.

Raises ValueError if fabric_name is not set.
"""
if self.fabric_name is None:
raise ValueError(f"{type(self).__name__}.path: fabric_name must be set before accessing path.")
base_path = BasePath.path("fabrics", self.fabric_name, self._path_suffix)
query_string = self.endpoint_params.to_query_string()
if query_string:
return f"{base_path}?{query_string}"
return base_path


class EpManageFabricGroupMembersGet(_EpManageFabricGroupMembersBase):
"""
# Summary

ND Manage Fabric Group Members GET Endpoint

## Description

Endpoint to retrieve members of a fabric group from the ND Manage service.
The fabric name (group name) is a required path parameter.

## Path

- /api/v1/manage/fabrics/{fabric_name}/members

## Verb

- GET

## Raises

- `ValueError` if `fabric_name` is not set when accessing `path`

## Usage

```python
request = EpManageFabricGroupMembersGet()
request.fabric_name = "my-fabric-group"
path = request.path
verb = request.verb
# Path: /api/v1/manage/fabrics/my-fabric-group/members
```
"""

_path_suffix: ClassVar[str] = "members"

class_name: Literal["EpManageFabricGroupMembersGet"] = Field(
default="EpManageFabricGroupMembersGet",
description="Class name for backward compatibility",
)

@property
def verb(self) -> HttpVerbEnum:
"""Return the HTTP verb for this endpoint."""
return HttpVerbEnum.GET


class EpManageFabricGroupMembersAddPost(_EpManageFabricGroupMembersBase):
"""
# Summary

ND Manage Fabric Group Members Add POST Endpoint

## Description

Endpoint to add members to a fabric group via the ND Manage service.
The fabric name (group name) is a required path parameter.

## Path

- /api/v1/manage/fabrics/{fabric_name}/actions/addMembers

## Verb

- POST

## Request Body (application/json)

```json
{
"members": [
{ "name": "member-fabric-name" }
]
}
```

## Raises

- `ValueError` if `fabric_name` is not set when accessing `path`

## Usage

```python
request = EpManageFabricGroupMembersAddPost()
request.fabric_name = "my-fabric-group"
path = request.path
verb = request.verb
# Path: /api/v1/manage/fabrics/my-fabric-group/actions/addMembers
```
"""

_path_suffix: ClassVar[str] = "actions/addMembers"

class_name: Literal["EpManageFabricGroupMembersAddPost"] = Field(
default="EpManageFabricGroupMembersAddPost",
description="Class name for backward compatibility",
)

@property
def verb(self) -> HttpVerbEnum:
"""Return the HTTP verb for this endpoint."""
return HttpVerbEnum.POST


class EpManageFabricGroupMembersRemovePost(_EpManageFabricGroupMembersBase):
"""
# Summary

ND Manage Fabric Group Members Remove POST Endpoint

## Description

Endpoint to remove members from a fabric group via the ND Manage service.
The fabric name (group name) is a required path parameter.

## Path

- /api/v1/manage/fabrics/{fabric_name}/actions/removeMembers

## Verb

- POST

## Request Body (application/json)

```json
{
"members": [
{ "name": "member-fabric-name" }
]
}
```

## Raises

- `ValueError` if `fabric_name` is not set when accessing `path`

## Usage

```python
request = EpManageFabricGroupMembersRemovePost()
request.fabric_name = "my-fabric-group"
path = request.path
verb = request.verb
# Path: /api/v1/manage/fabrics/my-fabric-group/actions/removeMembers
```
"""

_path_suffix: ClassVar[str] = "actions/removeMembers"

class_name: Literal["EpManageFabricGroupMembersRemovePost"] = Field(
default="EpManageFabricGroupMembersRemovePost",
description="Class name for backward compatibility",
)

@property
def verb(self) -> HttpVerbEnum:
"""Return the HTTP verb for this endpoint."""
return HttpVerbEnum.POST
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright: (c) 2026, Matt Tarkington (@mtarking)

# 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

from typing import List, Dict, Any, Optional, ClassVar, Literal, Set
from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import (
Field,
ConfigDict,
)
from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel


class FabricGroupMemberModel(NDBaseModel):
"""
Fabric group member configuration for Nexus Dashboard.

Represents a single member fabric within a fabric group.

Identifier: name (single)

API details:
- Members are added via POST /fabrics/{fabricName}/actions/addMembers
- Members are removed via POST /fabrics/{fabricName}/actions/removeMembers
- Members are queried via GET /fabrics/{fabricName}/members
- The parent fabric group name is a module-level parameter, not part of
the member model itself.
"""

model_config = ConfigDict(populate_by_name=True)

# --- Identifier Configuration ---

identifiers: ClassVar[Optional[List[str]]] = ["name"]
identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single"

# --- Serialization Configuration ---

exclude_from_diff: ClassVar[Set[str]] = set()
payload_exclude_fields: ClassVar[Set[str]] = {"fabric_type"}

# --- Fields ---

name: str = Field(alias="name")
fabric_type: Optional[str] = Field(default=None, alias="type")

# --- Argument Spec ---

@classmethod
def get_argument_spec(cls) -> Dict[str, Any]:
return dict(
fabric_name=dict(type="str", required=True),
config=dict(
type="list",
elements="dict",
required=True,
options=dict(
name=dict(type="str", required=True),
),
),
state=dict(
type="str",
default="merged",
choices=["merged", "deleted", "gathered"],
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright: (c) 2026, Matt Tarkington (@mtarking)

# 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

from typing import Type, ClassVar, List, Optional
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_group.manage_fabric_group_members import FabricGroupMemberModel
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_fabric_group_members import (
EpManageFabricGroupMembersGet,
EpManageFabricGroupMembersAddPost,
EpManageFabricGroupMembersRemovePost,
)


class ManageFabricGroupMembersOrchestrator(NDBaseOrchestrator[FabricGroupMemberModel]):
model_class: ClassVar[Type[NDBaseModel]] = FabricGroupMemberModel
supports_bulk_create: ClassVar[bool] = True
supports_bulk_delete: ClassVar[bool] = True

create_endpoint: Type[NDEndpointBaseModel] = EpManageFabricGroupMembersAddPost
update_endpoint: Type[NDEndpointBaseModel] = EpManageFabricGroupMembersAddPost
delete_endpoint: Type[NDEndpointBaseModel] = EpManageFabricGroupMembersRemovePost
query_one_endpoint: Type[NDEndpointBaseModel] = EpManageFabricGroupMembersGet
query_all_endpoint: Type[NDEndpointBaseModel] = EpManageFabricGroupMembersGet
create_bulk_endpoint: Optional[Type[NDEndpointBaseModel]] = EpManageFabricGroupMembersAddPost
delete_bulk_endpoint: Optional[Type[NDEndpointBaseModel]] = EpManageFabricGroupMembersRemovePost

def _get_fabric_group_name(self) -> str:
"""Extract fabric_name from module params."""
return self.sender.params.get("fabric_name")

def create_bulk(self, model_instances: List[FabricGroupMemberModel], **kwargs) -> ResponseType:
"""
Add members to the fabric group in a single API call.

Wraps all member names in the required API payload format:
{"members": [{"name": "m1"}, {"name": "m2"}, ...]}
"""
try:
api_endpoint = self.create_bulk_endpoint()
api_endpoint.fabric_name = self._get_fabric_group_name()
payload = {"members": [{"name": instance.name} for instance in model_instances]}
return self.sender.request(path=api_endpoint.path, method=api_endpoint.verb, data=payload)
except Exception as e:
names = [instance.name for instance in model_instances]
raise Exception(f"Add members failed for {names}: {e}") from e

def delete_bulk(self, model_instances: List[FabricGroupMemberModel], **kwargs) -> ResponseType:
"""
Remove members from the fabric group in a single API call.

Wraps all member names in the required API payload format:
{"members": [{"name": "m1"}, {"name": "m2"}, ...]}
"""
try:
api_endpoint = self.delete_bulk_endpoint()
api_endpoint.fabric_name = self._get_fabric_group_name()
payload = {"members": [{"name": instance.name} for instance in model_instances]}
return self.sender.request(path=api_endpoint.path, method=api_endpoint.verb, data=payload)
except Exception as e:
names = [instance.name for instance in model_instances]
raise Exception(f"Remove members failed for {names}: {e}") from e

def query_one(self, model_instance: FabricGroupMemberModel, **kwargs) -> ResponseType:
"""
Query a specific member of the fabric group by checking the full members list.
"""
try:
all_members = self.query_all()
for member in all_members:
if member.get("name") == model_instance.name:
return member
return None
except Exception as e:
raise Exception(f"Query member failed for {model_instance.name}: {e}") from e

def query_all(self, model_instance=None, **kwargs) -> ResponseType:
"""
Query all members of the fabric group.

Extracts 'fabrics' from the API response.
"""
try:
api_endpoint = self.query_all_endpoint()
api_endpoint.fabric_name = self._get_fabric_group_name()
result = self.sender.query_obj(api_endpoint.path)
return result.get("fabrics", []) or []
except Exception as e:
raise Exception(f"Query all members failed: {e}") from e
Loading
Loading