Repository: aristanetworks/anta
Pull request #903 (2024-10-28 19:28:46)

fix(anta)!: Fix tags behavior to reflect the documentation
# Description

#827 fixed multiple `--tags` CLI support but introduced an issue where test-to-device tag matching wasn't properly enforced.

This PR now reflects the behavior specified in the [Tag Management ](https://anta.arista.com/stable/cli/tag-management/#using-tags) documentation.



# Checklist:

<!-- Delete not relevant items !-->

- [X] My code follows the style guidelines of this project
- [X] I have performed a self-review of my code
- [X] I have run pre-commit for code linting and typing (`pre-commit run`)
- [X] I have made corresponding changes to the documentation
- [X] I have added tests that prove my fix is effective or that my feature works
- [X] New and existing unit tests pass locally with my changes (`tox -e testenv`)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903)


codspeed-hq[bot]: ## [CodSpeed Performance Report](https://codspeed.io/aristanetworks/anta/branches/carl-baillargeon:fix/tags_again)

### Merging #903 will **not alter performance**

<sub>Comparing <code>carl-baillargeon:fix/tags_again</code> (c6e6a39) with <code>main</code> (a76ae07)</sub>



### Summary

`✅ 8` untouched benchmarks





sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=aristanetworks_anta&pullRequest=903&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=aristanetworks_anta&pullRequest=903&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=aristanetworks_anta&pullRequest=903&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=aristanetworks_anta&pullRequest=903)



Patch:
diff --git a/anta/runner.py b/anta/runner.py
index 0147c3cc..7b0eadf7 100644
--- a/anta/runner.py
+++ b/anta/runner.py
@@ -146,20 +146,26 @@ def prepare_tests(
     # Using a set to avoid inserting duplicate tests
     device_to_tests: defaultdict[AntaDevice, set[AntaTestDefinition]] = defaultdict(set)
 
+    total_test_count = 0
+
     # Create the device to tests mapping from the tags
     for device in inventory.devices:
         if tags:
-            if not any(tag in device.tags for tag in tags):
+            # If there are CLI tags, execute tests with matching tags for this device
+            if not (matching_tags := tags.intersection(device.tags)):
                 # The device does not have any selected tag, skipping
                 continue
+            device_to_tests[device].update(catalog.get_tests_by_tags(matching_tags))
         else:
             # If there is no CLI tags, execute all tests that do not have any tags
             device_to_tests[device].update(catalog.tag_to_tests[None])
 
-        # Add the tests with matching tags from device tags
-        device_to_tests[device].update(catalog.get_tests_by_tags(device.tags))
+            # Then add the tests with matching tags from device tags
+            device_to_tests[device].update(catalog.get_tests_by_tags(device.tags))
+
+        total_test_count += len(device_to_tests[device])
 
-    if len(device_to_tests.values()) == 0:
+    if total_test_count == 0:
         msg = (
             f"There are no tests{f' matching the tags {tags} ' if tags else ' '}to run in the current test catalog and device inventory, please verify your inputs."
         )
diff --git a/docs/cli/tag-management.md b/docs/cli/tag-management.md
index ad5ccf3a..4108d75b 100644
--- a/docs/cli/tag-management.md
+++ b/docs/cli/tag-management.md
@@ -4,9 +4,7 @@
   ~ that can be found in the LICENSE file.
   -->
 
-ANTA commands can be used with a `--tags` option. This option **filters the inventory** with the specified tag(s) when running the command.
-
-Tags can also be used to **restrict a specific test** to a set of devices when using `anta nrfu`.
+ANTA uses tags to define test-to-device mappings (tests run on devices with matching tags) and the `--tags` CLI option acts as a filter to execute specific test/device combinations.
 
 ## Defining tags
 


Test patch:
diff --git a/tests/data/test_inventory_with_tags.yml b/tests/data/test_inventory_with_tags.yml
index cbbcd75e..16a9df4c 100644
--- a/tests/data/test_inventory_with_tags.yml
+++ b/tests/data/test_inventory_with_tags.yml
@@ -3,7 +3,7 @@ anta_inventory:
   hosts:
     - name: leaf1
       host: leaf1.anta.arista.com
-      tags: ["leaf"]
+      tags: ["leaf", "dc1"]
     - name: leaf2
       host: leaf2.anta.arista.com
       tags: ["leaf"]
diff --git a/tests/units/test_runner.py b/tests/units/test_runner.py
index b80259cc..8d19a4d1 100644
--- a/tests/units/test_runner.py
+++ b/tests/units/test_runner.py
@@ -138,6 +138,7 @@ def test_adjust_rlimit_nofile_invalid_env(caplog: pytest.LogCaptureFixture) -> N
         pytest.param({"filename": "test_inventory_with_tags.yml"}, None, {"VerifyMlagStatus", "VerifyUptime"}, 3, 5, id="filtered-tests"),
         pytest.param({"filename": "test_inventory_with_tags.yml"}, {"leaf"}, {"VerifyMlagStatus", "VerifyUptime"}, 2, 4, id="1-tag-filtered-tests"),
         pytest.param({"filename": "test_inventory_with_tags.yml"}, {"invalid"}, None, 0, 0, id="invalid-tag"),
+        pytest.param({"filename": "test_inventory_with_tags.yml"}, {"dc1"}, None, 0, 0, id="device-tag-no-tests"),
     ],
     indirect=["inventory"],
 )