Skip to content

Commit

Permalink
feat(anta.tests): Add test to verify mcs server mount states (#961)
Browse files Browse the repository at this point in the history
  • Loading branch information
sarunac authored Dec 16, 2024
1 parent 9876833 commit 8f69f64
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 12 deletions.
81 changes: 78 additions & 3 deletions anta/tests/cvx.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# mypy: disable-error-code=attr-defined
from __future__ import annotations

from typing import TYPE_CHECKING, ClassVar, Literal
from typing import TYPE_CHECKING, Any, ClassVar, Literal

from anta.custom_types import PositiveInteger
from anta.models import AntaCommand, AntaTest
Expand Down Expand Up @@ -91,6 +91,81 @@ def test(self) -> None:
self.result.is_failure(f"Management CVX status is not valid: {cluster_state}")


class VerifyMcsServerMounts(AntaTest):
"""Verify if all MCS server mounts are in a MountComplete state.
Expected Results
----------------
* Success: The test will pass if all the MCS mount status on MCS server are mountStateMountComplete.
* Failure: The test will fail even if any MCS server mount status is not mountStateMountComplete.
Examples
--------
```yaml
anta.tests.cvx:
- VerifyMcsServerMounts:
connections_count: 100
```
"""

categories: ClassVar[list[str]] = ["cvx"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show cvx mounts", revision=1)]

mcs_path_types: ClassVar[list[str]] = ["Mcs::ApiConfigRedundancyStatus", "Mcs::ActiveFlows", "Mcs::Client::Status"]
"""The list of expected MCS path types to verify."""

class Input(AntaTest.Input):
"""Input model for the VerifyMcsServerMounts test."""

connections_count: int
"""The expected number of active CVX Connections with mountStateMountComplete"""

def validate_mount_states(self, mount: dict[str, Any], hostname: str) -> None:
"""Validate the mount states of a given mount."""
mount_states = mount["mountStates"][0]

if (num_path_states := len(mount_states["pathStates"])) != (expected_num := len(self.mcs_path_types)):
self.result.is_failure(f"Incorrect number of mount path states for {hostname} - Expected: {expected_num}, Actual: {num_path_states}")

for path in mount_states["pathStates"]:
if (path_type := path.get("type")) not in self.mcs_path_types:
self.result.is_failure(f"Unexpected MCS path type for {hostname}: '{path_type}'.")
if (path_state := path.get("state")) != "mountStateMountComplete":
self.result.is_failure(f"MCS server mount state for path '{path_type}' is not valid is for {hostname}: '{path_state}'.")

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifyMcsServerMounts."""
command_output = self.instance_commands[0].json_output
self.result.is_success()
active_count = 0

if not (connections := command_output.get("connections")):
self.result.is_failure("CVX connections are not available.")
return

for connection in connections:
mounts = connection.get("mounts", [])
hostname = connection["hostname"]

mcs_mounts = [mount for mount in mounts if mount["service"] == "Mcs"]

if not mounts:
self.result.is_failure(f"No mount status for {hostname}")
continue

if not mcs_mounts:
self.result.is_failure(f"MCS mount state not detected for {hostname}")
else:
for mount in mcs_mounts:
self.validate_mount_states(mount, hostname)
active_count += 1

if active_count != self.inputs.connections_count:
self.result.is_failure(f"Incorrect CVX successful connections count. Expected: {self.inputs.connections_count}, Actual : {active_count}")


class VerifyActiveCVXConnections(AntaTest):
"""Verifies the number of active CVX Connections.
Expand All @@ -116,7 +191,7 @@ class Input(AntaTest.Input):
"""Input model for the VerifyActiveCVXConnections test."""

connections_count: PositiveInteger
"""The expected number of active CVX Connections"""
"""The expected number of active CVX Connections."""

@AntaTest.anta_test
def test(self) -> None:
Expand All @@ -131,7 +206,7 @@ def test(self) -> None:
active_count = len([connection for connection in connections if connection.get("oobConnectionActive")])

if self.inputs.connections_count != active_count:
self.result.is_failure(f"CVX active connections count. Expected: {self.inputs.connections_count} , Actual : {active_count}")
self.result.is_failure(f"CVX active connections count. Expected: {self.inputs.connections_count}, Actual : {active_count}")


class VerifyCVXClusterStatus(AntaTest):
Expand Down
3 changes: 3 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ anta.tests.cvx:
enabled: true
- VerifyMcsClientMounts:
# Verify if all MCS client mounts are in mountStateMountComplete.
- VerifyMcsServerMounts:
# Verify if all MCS server mounts are in a MountComplete state.
connections_count: 100
anta.tests.field_notices:
- VerifyFieldNotice44Resolution:
# Verifies that the device is using the correct Aboot version per FN0044.
Expand Down
180 changes: 171 additions & 9 deletions tests/units/anta_tests/test_cvx.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Any

from anta.tests.cvx import VerifyActiveCVXConnections, VerifyCVXClusterStatus, VerifyManagementCVX, VerifyMcsClientMounts
from anta.tests.cvx import VerifyActiveCVXConnections, VerifyCVXClusterStatus, VerifyManagementCVX, VerifyMcsClientMounts, VerifyMcsServerMounts
from tests.units.anta_tests import test

DATA: list[dict[str, Any]] = [
Expand Down Expand Up @@ -146,6 +146,175 @@
"inputs": {"enabled": False},
"expected": {"result": "failure", "messages": ["Management CVX status is not valid: None"]},
},
{
"name": "failure - no clusterStatus",
"test": VerifyManagementCVX,
"eos_data": [{}],
"inputs": {"enabled": False},
"expected": {"result": "failure", "messages": ["Management CVX status is not valid: None"]},
},
{
"name": "success",
"test": VerifyMcsServerMounts,
"eos_data": [
{
"connections": [
{
"hostname": "media-leaf-1",
"mounts": [
{
"service": "Mcs",
"mountStates": [
{
"pathStates": [
{"path": "mcs/v1/apiCfgRedStatus", "type": "Mcs::ApiConfigRedundancyStatus", "state": "mountStateMountComplete"},
{"path": "mcs/v1/activeflows", "type": "Mcs::ActiveFlows", "state": "mountStateMountComplete"},
{"path": "mcs/switch/status", "type": "Mcs::Client::Status", "state": "mountStateMountComplete"},
]
}
],
}
],
}
]
}
],
"inputs": {"connections_count": 1},
"expected": {"result": "success"},
},
{
"name": "failure-no-mounts",
"test": VerifyMcsServerMounts,
"eos_data": [{"connections": [{"hostname": "media-leaf-1", "mounts": []}]}],
"inputs": {"connections_count": 1},
"expected": {
"result": "failure",
"messages": ["No mount status for media-leaf-1", "Incorrect CVX successful connections count. Expected: 1, Actual : 0"],
},
},
{
"name": "failure-unexpected-number-paths",
"test": VerifyMcsServerMounts,
"eos_data": [
{
"connections": [
{
"hostname": "media-leaf-1",
"mounts": [
{
"service": "Mcs",
"mountStates": [
{
"pathStates": [
{"path": "mcs/v1/apiCfgRedStatus", "type": "Mcs::ApiStatus", "state": "mountStateMountComplete"},
{"path": "mcs/v1/activeflows", "type": "Mcs::ActiveFlows", "state": "mountStateMountComplete"},
]
}
],
}
],
}
]
}
],
"inputs": {"connections_count": 1},
"expected": {
"result": "failure",
"messages": [
"Incorrect number of mount path states for media-leaf-1 - Expected: 3, Actual: 2",
"Unexpected MCS path type for media-leaf-1: 'Mcs::ApiStatus'.",
],
},
},
{
"name": "failure-unexpected-path-type",
"test": VerifyMcsServerMounts,
"eos_data": [
{
"connections": [
{
"hostname": "media-leaf-1",
"mounts": [
{
"service": "Mcs",
"mountStates": [
{
"pathStates": [
{"path": "mcs/v1/apiCfgRedStatus", "type": "Mcs::ApiStatus", "state": "mountStateMountComplete"},
{"path": "mcs/v1/activeflows", "type": "Mcs::ActiveFlows", "state": "mountStateMountComplete"},
{"path": "mcs/switch/status", "type": "Mcs::Client::Status", "state": "mountStateMountComplete"},
]
}
],
}
],
}
]
}
],
"inputs": {"connections_count": 1},
"expected": {"result": "failure", "messages": ["Unexpected MCS path type for media-leaf-1: 'Mcs::ApiStatus'"]},
},
{
"name": "failure-invalid-mount-state",
"test": VerifyMcsServerMounts,
"eos_data": [
{
"connections": [
{
"hostname": "media-leaf-1",
"mounts": [
{
"service": "Mcs",
"mountStates": [
{
"pathStates": [
{"path": "mcs/v1/apiCfgRedStatus", "type": "Mcs::ApiConfigRedundancyStatus", "state": "mountStateMountFailed"},
{"path": "mcs/v1/activeflows", "type": "Mcs::ActiveFlows", "state": "mountStateMountComplete"},
{"path": "mcs/switch/status", "type": "Mcs::Client::Status", "state": "mountStateMountComplete"},
]
}
],
}
],
}
]
}
],
"inputs": {"connections_count": 1},
"expected": {
"result": "failure",
"messages": ["MCS server mount state for path 'Mcs::ApiConfigRedundancyStatus' is not valid is for media-leaf-1: 'mountStateMountFailed'"],
},
},
{
"name": "failure-no-mcs-mount",
"test": VerifyMcsServerMounts,
"eos_data": [
{
"connections": [
{
"hostname": "media-leaf-1",
"mounts": [
{
"service": "blah-blah",
"mountStates": [{"pathStates": [{"path": "blah-blah-path", "type": "blah-blah-type", "state": "blah-blah-state"}]}],
}
],
}
]
}
],
"inputs": {"connections_count": 1},
"expected": {"result": "failure", "messages": ["MCS mount state not detected", "Incorrect CVX successful connections count. Expected: 1, Actual : 0"]},
},
{
"name": "failure-connections",
"test": VerifyMcsServerMounts,
"eos_data": [{}],
"inputs": {"connections_count": 1},
"expected": {"result": "failure", "messages": ["CVX connections are not available."]},
},
{
"name": "success",
"test": VerifyActiveCVXConnections,
Expand Down Expand Up @@ -188,7 +357,7 @@
}
],
"inputs": {"connections_count": 2},
"expected": {"result": "failure", "messages": ["CVX active connections count. Expected: 2 , Actual : 1"]},
"expected": {"result": "failure", "messages": ["CVX active connections count. Expected: 2, Actual : 1"]},
},
{
"name": "failure-no-connections",
Expand All @@ -197,13 +366,6 @@
"inputs": {"connections_count": 2},
"expected": {"result": "failure", "messages": ["CVX connections are not available"]},
},
{
"name": "failure - no clusterStatus",
"test": VerifyManagementCVX,
"eos_data": [{}],
"inputs": {"enabled": False},
"expected": {"result": "failure", "messages": ["Management CVX status is not valid: None"]},
},
{
"name": "success-all",
"test": VerifyCVXClusterStatus,
Expand Down

0 comments on commit 8f69f64

Please sign in to comment.