summaryrefslogtreecommitdiffstats
path: root/anta/tests/hardware.py
blob: 0a149f28a97b03ed4de5134f780af16048361f75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# Copyright (c) 2023-2024 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
"""
Test functions related to the hardware or environment
"""
# Mypy does not understand AntaTest.Input typing
# mypy: disable-error-code=attr-defined
from __future__ import annotations

# Need to keep List for pydantic in python 3.8
from typing import List

from anta.decorators import skip_on_platforms
from anta.models import AntaCommand, AntaTest


class VerifyTransceiversManufacturers(AntaTest):
    """
    This test verifies if all the transceivers come from approved manufacturers.

    Expected Results:
      * success: The test will pass if all transceivers are from approved manufacturers.
      * failure: The test will fail if some transceivers are from unapproved manufacturers.
    """

    name = "VerifyTransceiversManufacturers"
    description = "Verifies if all transceivers come from approved manufacturers."
    categories = ["hardware"]
    commands = [AntaCommand(command="show inventory", ofmt="json")]

    class Input(AntaTest.Input):  # pylint: disable=missing-class-docstring
        manufacturers: List[str]
        """List of approved transceivers manufacturers"""

    @skip_on_platforms(["cEOSLab", "vEOS-lab"])
    @AntaTest.anta_test
    def test(self) -> None:
        command_output = self.instance_commands[0].json_output
        wrong_manufacturers = {
            interface: value["mfgName"] for interface, value in command_output["xcvrSlots"].items() if value["mfgName"] not in self.inputs.manufacturers
        }
        if not wrong_manufacturers:
            self.result.is_success()
        else:
            self.result.is_failure(f"Some transceivers are from unapproved manufacturers: {wrong_manufacturers}")


class VerifyTemperature(AntaTest):
    """
    This test verifies if the device temperature is within acceptable limits.

    Expected Results:
      * success: The test will pass if the device temperature is currently OK: 'temperatureOk'.
      * failure: The test will fail if the device temperature is NOT OK.
    """

    name = "VerifyTemperature"
    description = "Verifies the device temperature."
    categories = ["hardware"]
    commands = [AntaCommand(command="show system environment temperature", ofmt="json")]

    @skip_on_platforms(["cEOSLab", "vEOS-lab"])
    @AntaTest.anta_test
    def test(self) -> None:
        command_output = self.instance_commands[0].json_output
        temperature_status = command_output["systemStatus"] if "systemStatus" in command_output.keys() else ""
        if temperature_status == "temperatureOk":
            self.result.is_success()
        else:
            self.result.is_failure(f"Device temperature exceeds acceptable limits. Current system status: '{temperature_status}'")


class VerifyTransceiversTemperature(AntaTest):
    """
    This test verifies if all the transceivers are operating at an acceptable temperature.

    Expected Results:
          * success: The test will pass if all transceivers status are OK: 'ok'.
          * failure: The test will fail if some transceivers are NOT OK.
    """

    name = "VerifyTransceiversTemperature"
    description = "Verifies the transceivers temperature."
    categories = ["hardware"]
    commands = [AntaCommand(command="show system environment temperature transceiver", ofmt="json")]

    @skip_on_platforms(["cEOSLab", "vEOS-lab"])
    @AntaTest.anta_test
    def test(self) -> None:
        command_output = self.instance_commands[0].json_output
        sensors = command_output["tempSensors"] if "tempSensors" in command_output.keys() else ""
        wrong_sensors = {
            sensor["name"]: {
                "hwStatus": sensor["hwStatus"],
                "alertCount": sensor["alertCount"],
            }
            for sensor in sensors
            if sensor["hwStatus"] != "ok" or sensor["alertCount"] != 0
        }
        if not wrong_sensors:
            self.result.is_success()
        else:
            self.result.is_failure(f"The following sensors are operating outside the acceptable temperature range or have raised alerts: {wrong_sensors}")


class VerifyEnvironmentSystemCooling(AntaTest):
    """
    This test verifies the device's system cooling.

    Expected Results:
      * success: The test will pass if the system cooling status is OK: 'coolingOk'.
      * failure: The test will fail if the system cooling status is NOT OK.
    """

    name = "VerifyEnvironmentSystemCooling"
    description = "Verifies the system cooling status."
    categories = ["hardware"]
    commands = [AntaCommand(command="show system environment cooling", ofmt="json")]

    @skip_on_platforms(["cEOSLab", "vEOS-lab"])
    @AntaTest.anta_test
    def test(self) -> None:
        command_output = self.instance_commands[0].json_output
        sys_status = command_output["systemStatus"] if "systemStatus" in command_output.keys() else ""
        self.result.is_success()
        if sys_status != "coolingOk":
            self.result.is_failure(f"Device system cooling is not OK: '{sys_status}'")


class VerifyEnvironmentCooling(AntaTest):
    """
    This test verifies the fans status.

    Expected Results:
      * success: The test will pass if the fans status are within the accepted states list.
      * failure: The test will fail if some fans status is not within the accepted states list.
    """

    name = "VerifyEnvironmentCooling"
    description = "Verifies the status of power supply fans and all fan trays."
    categories = ["hardware"]
    commands = [AntaCommand(command="show system environment cooling", ofmt="json")]

    class Input(AntaTest.Input):  # pylint: disable=missing-class-docstring
        states: List[str]
        """Accepted states list for fan status"""

    @skip_on_platforms(["cEOSLab", "vEOS-lab"])
    @AntaTest.anta_test
    def test(self) -> None:
        command_output = self.instance_commands[0].json_output
        self.result.is_success()
        # First go through power supplies fans
        for power_supply in command_output.get("powerSupplySlots", []):
            for fan in power_supply.get("fans", []):
                if (state := fan["status"]) not in self.inputs.states:
                    self.result.is_failure(f"Fan {fan['label']} on PowerSupply {power_supply['label']} is: '{state}'")
        # Then go through fan trays
        for fan_tray in command_output.get("fanTraySlots", []):
            for fan in fan_tray.get("fans", []):
                if (state := fan["status"]) not in self.inputs.states:
                    self.result.is_failure(f"Fan {fan['label']} on Fan Tray {fan_tray['label']} is: '{state}'")


class VerifyEnvironmentPower(AntaTest):
    """
    This test verifies the power supplies status.

    Expected Results:
      * success: The test will pass if the power supplies status are within the accepted states list.
      * failure: The test will fail if some power supplies status is not within the accepted states list.
    """

    name = "VerifyEnvironmentPower"
    description = "Verifies the power supplies status."
    categories = ["hardware"]
    commands = [AntaCommand(command="show system environment power", ofmt="json")]

    class Input(AntaTest.Input):  # pylint: disable=missing-class-docstring
        states: List[str]
        """Accepted states list for power supplies status"""

    @skip_on_platforms(["cEOSLab", "vEOS-lab"])
    @AntaTest.anta_test
    def test(self) -> None:
        command_output = self.instance_commands[0].json_output
        power_supplies = command_output["powerSupplies"] if "powerSupplies" in command_output.keys() else "{}"
        wrong_power_supplies = {
            powersupply: {"state": value["state"]} for powersupply, value in dict(power_supplies).items() if value["state"] not in self.inputs.states
        }
        if not wrong_power_supplies:
            self.result.is_success()
        else:
            self.result.is_failure(f"The following power supplies status are not in the accepted states list: {wrong_power_supplies}")


class VerifyAdverseDrops(AntaTest):
    """
    This test verifies if there are no adverse drops on DCS7280E and DCS7500E.

    Expected Results:
      * success: The test will pass if there are no adverse drops.
      * failure: The test will fail if there are adverse drops.
    """

    name = "VerifyAdverseDrops"
    description = "Verifies there are no adverse drops on DCS7280E and DCS7500E"
    categories = ["hardware"]
    commands = [AntaCommand(command="show hardware counter drop", ofmt="json")]

    @skip_on_platforms(["cEOSLab", "vEOS-lab"])
    @AntaTest.anta_test
    def test(self) -> None:
        command_output = self.instance_commands[0].json_output
        total_adverse_drop = command_output["totalAdverseDrops"] if "totalAdverseDrops" in command_output.keys() else ""
        if total_adverse_drop == 0:
            self.result.is_success()
        else:
            self.result.is_failure(f"Device totalAdverseDrops counter is: '{total_adverse_drop}'")