Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anta): Added the test case to verify NTP associations functionality #757

Merged
merged 7 commits into from
Sep 4, 2024

Conversation

vitthalmagadum
Copy link
Contributor

Description

NTP associations for all NTP servers should be up as denoted by the "condition" field below, and the primary NTP server should be preferred.

primary server will have condition "sys.peer", and pri server is denoted using "preferred: true "
other servers will have condition "candidate"

Fixes #753

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have run pre-commit for code linting and typing (pre-commit run)
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes (tox -e testenv)

@vitthalmagadum vitthalmagadum marked this pull request as draft July 18, 2024 06:07
@vitthalmagadum vitthalmagadum changed the title Added the test case to verify NTP associations functionality feat(anta): Added the test case to verify NTP associations functionality Jul 18, 2024
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifyNTPAssociations."""
failures: dict[str, Any] = {"ntp_servers": {}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not need to be a dict with a top level key ntp_servers, the key does not bring any value. you can make it a list of dict

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled

for ntp_server in self.inputs.ntp_servers:
address = str(ntp_server.server_address)
preferred = ntp_server.preferred
failure: dict[str, dict[str, dict[str, Any]]] = {"ntp_servers": {address: {}}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why build a nested structure on every iteration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled

Comment on lines 364 to 365
failure["ntp_servers"][address] = {"status": "Not configured"}
failures = deep_update(failures, failure)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once failures is a dict, you can simply do this

Suggested change
failure["ntp_servers"][address] = {"status": "Not configured"}
failures = deep_update(failures, failure)
failures[address] = {"status": "Not configured"}})

Though it feels weird we have status vs condition would be better to have a single way of rendering things to be easier to manipulate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled

condition = get_value(peer_detail, "condition")
if not preferred and condition != "candidate":
failure["ntp_servers"][address] = {"condition": "Not candidate"}
failures = deep_update(failures, failure)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please try to avoid to use deep_update, no need to unnecessarily nest structures here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled

anta/tests/system.py Outdated Show resolved Hide resolved
anta/tests/system.py Outdated Show resolved Hide resolved
class NTPServer(BaseModel):
"""Model for a NTP server."""

server_address: IPv4Address
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user configuring the pool then they wont aware about the ip address. So instant of checking the condition on basis of peer address can we check the peers.

leaf1-dc1(config)#show ntp associations | json
{
    "peers": {
        "192.168.66.21": {
            "condition": "reject",
            "peerIpAddr": "192.168.66.21",
            "refid": ".INIT.",
            "stratumLevel": 16,
            "peerType": "unicast",
            "lastReceived": 1721650565.0,
            "pollInterval": 1024,
            "reachabilityHistory": [
                false
            ],
            "delay": 0.0,
            "offset": 0.0,
            "jitter": 0.0
        },
        "ntp3.aristanetworks.com": {
            "condition": "sys.peer",
            "peerIpAddr": "10.41.194.20",
            "refid": "17.253.16.125",
            "stratumLevel": 2,
            "peerType": "unicast",
            },
    }

In that case server address can be str or IPv4Address.
@carl-baillargeon @gmuloc

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we can have a model that looks like either

class NTPServer(BaseModel):
    server_address: IPv4Address | None
    server_name: str | None
    preferred: bool = False

and we use whichever is available between address and name (or both to check the peer IP)

or

class NTPServer(BaseModel):
    server: str | IPv4Address
    preferred: bool = False

and we are more limited in what we check but we can move forward

An additional problem is that even the server address can be wrong as we have seen in the example you shared (e.g. - you add a generic server name ntp.aristanetworks.com and a more specific one appears in your device output ntp3.aristanetworks.com)
And this we cannot really handle in ANTA I think (because it all depends on DNS resolution) - probably this caveat should be documented in the test. @vitthalmagadum can you add some note stating the same?

anta/tests/system.py Outdated Show resolved Hide resolved
anta/tests/system.py Outdated Show resolved Hide resolved
anta/tests/system.py Outdated Show resolved Hide resolved
anta/tests/system.py Outdated Show resolved Hide resolved
examples/tests.yaml Outdated Show resolved Hide resolved
tests/units/anta_tests/test_system.py Show resolved Hide resolved
@vitthalmagadum vitthalmagadum force-pushed the issue_753 branch 2 times, most recently from 89e775a to 64979b4 Compare August 20, 2024 16:27
@vitthalmagadum vitthalmagadum marked this pull request as ready for review August 20, 2024 16:32

# Check if NTP server details exists.
if (peer_detail := get_value(peer_details, server_address, separator="..")) is None:
failures += f"\nNTP peer {server_address} is not configured."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like having unnecessary carriage returns in the result messages. When there is only 1 message:
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the unnecessary \n at start. Thanks!!

"expected": {
"result": "failure",
"messages": [
"\nFor NTP peer 1.1.1.1:\nExpected `sys.peer` as the condition, but found `candidate` instead.\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here there will be an empty line at the beginning of the message which is unnecessary IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as well.

},
"expected": {
"result": "failure",
"messages": ["NTP peers are not configured."],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of NTP peers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

class NTPServer(BaseModel):
"""Model for a NTP server."""

server_address: str | IPv4Address
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
server_address: str | IPv4Address
server_address: Hostname | IPv4Address

Import from custom_types.

Copy link
Contributor

@carl-baillargeon carl-baillargeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Minor change to typing for the inputs.

Copy link

sonarcloud bot commented Aug 30, 2024

@carl-baillargeon carl-baillargeon merged commit e7da54a into aristanetworks:main Sep 4, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add the test case to verify NTP associations functionality
4 participants