Remove hardcoded nodes for package checkers

Reworked test
Update pillar for nodes with rsync, with octavia
Removed rule for gnocchi and ujson because they should exist on all ctl* nodes
Added optimized call of the test.ping request for nodes

Fixes-Bug: #PROD-36531

Change-Id: I3512a8f159e40cfaa70dc5e6ab3673e5eb70ed64
diff --git a/test_set/cvp-sanity/fixtures/base.py b/test_set/cvp-sanity/fixtures/base.py
index bd62ed4..aabf8d1 100644
--- a/test_set/cvp-sanity/fixtures/base.py
+++ b/test_set/cvp-sanity/fixtures/base.py
@@ -25,7 +25,8 @@
 
 @pytest.fixture(scope='session')
 def local_salt_client():
-    return utils.init_salt_client()
+    pytest.local_salt_client = utils.init_salt_client()
+    return pytest.local_salt_client
 
 
 nodes = utils.calculate_groups()
diff --git a/test_set/cvp-sanity/tests/test_packet_checker.py b/test_set/cvp-sanity/tests/test_packet_checker.py
index 0b574cf..f534aad 100644
--- a/test_set/cvp-sanity/tests/test_packet_checker.py
+++ b/test_set/cvp-sanity/tests/test_packet_checker.py
@@ -2,14 +2,32 @@
 import json
 import utils
 import logging
+import itertools
+from functools import lru_cache
 
-def is_deb_in_exception(inconsistency_rule, package_name, error_node_list):
-    short_names_in_error_nodes = [n.split('.')[0] for n in error_node_list]
-    for node, excluded_packages in inconsistency_rule.items():
-        if package_name in excluded_packages and node in short_names_in_error_nodes:
+
+@lru_cache(maxsize=32)
+def targeted_minions(target):
+    """
+    Returns nodes associated with salt target
+    :param target: str, salt target in COMPOUND notation like I@nova:server
+    More here https://docs.saltproject.io/en/latest/topics/targeting/compound.html
+    :return: list of nodes or []
+    """
+    salt_client = pytest.local_salt_client
+    return list(salt_client.test_ping(
+        tgt=target,
+        expr_form='compound'))
+
+
+def is_deb_in_exception(inconsistency_rule, package_name, node_hostname):
+    for salt_target, excluded_packages in inconsistency_rule.items():
+        if package_name in excluded_packages \
+                and node_hostname in targeted_minions(salt_target):
             return True
     return False
 
+
 @pytest.mark.full
 def test_check_package_versions(local_salt_client, nodes_in_group):
     """Validate package has same versions on all the nodes.
@@ -19,73 +37,66 @@
      2) Exclude nodes without packages and exceptions
      3) Go through each package and save it with version from each node to the
         list. Mark 'No version' if package is not found.
-        If pachage name in the eception list or in inconsistency_rule, ignore it.
+        If package name in the exception list or in inconsistency_rule,
+        ignore it.
      4) Compare items in that list - they should be equal and match the amout of nodes
 
     """
-    # defines packages specific to the concrete nodes
-    inconsistency_rule = {"kvm03": ["rsync", "sysstat", "xz-utils"], "log01": ["python-elasticsearch"], "ctl01": ["python-gnocchiclient", "python-ujson"]}
+    salt = local_salt_client
+    # defines packages specific to the nodes
+    inconsistency_rule = {
+        "I@backupninja:server": [
+            "rsync", "sysstat", "xz-utils"],
+        "I@elasticsearch:server": [
+            "python-elasticsearch"],
+        # PROD-30833
+        "I@octavia:manager:controller_worker:loadbalancer_topology:SINGLE": [
+            "netfilter-persistent",
+            "gunicorn",
+            "octavia-worker",
+            "octavia-health-manager",
+            "octavia-housekeeping"]
+        }
     exclude_packages = utils.get_configuration().get("skipped_packages", [])
-    group, nodes = nodes_in_group
-    packages_versions = local_salt_client.cmd(tgt="L@"+','.join(nodes),
+
+    group_name, nodes = nodes_in_group
+    packages_versions_by_nodes = salt.cmd(tgt="L@"+','.join(nodes),
                                               fun='lowpkg.list_pkgs',
                                               expr_form='compound')
     # Let's exclude cid01 and dbs01 nodes from this check
-    exclude_nodes = list(local_salt_client.test_ping(tgt="I@galera:master or I@gerrit:client",
-                                                expr_form='compound').keys())
-    # PROD-30833
-    gtw01 = local_salt_client.pillar_get(
-        param='_param:openstack_gateway_node01_hostname') or 'gtw01'
-    cluster_domain = local_salt_client.pillar_get(
-        param='_param:cluster_domain') or '.local'
-    gtw01 += '.' + cluster_domain
-    if gtw01 in nodes:
-        octavia = local_salt_client.cmd(tgt="L@" + ','.join(nodes),
-                                        fun='pillar.get',
-                                        param='octavia:manager:enabled',
-                                        expr_form='compound')
-        gtws = [gtw for gtw in list(octavia.values()) if gtw]
-        if len(gtws) == 1:
-            exclude_nodes.append(gtw01)
-            logging.info("gtw01 node is skipped in test_check_package_versions")
+    exclude_nodes = targeted_minions("I@galera:master or I@gerrit:client")
 
-    total_nodes = [i for i in nodes if i not in exclude_nodes]
+    total_nodes = [i
+                   for i in nodes
+                   if i not in exclude_nodes]
     if len(total_nodes) < 2:
         pytest.skip("Nothing to compare - only 1 node")
-    nodes_with_packages = []
-    packages_with_different_versions = []
-    packages_names = set()
 
-    for node in total_nodes:
-        if not packages_versions[node]:
-            # TODO: do not skip node
-            logging.warning("Node {} is skipped".format(node))
-            continue
-        nodes_with_packages.append(node)
-        packages_names.update(list(packages_versions[node].keys()))
+    packages_with_different_versions = dict()
+    packages_names = set(itertools.chain.from_iterable(
+        [packages_versions_by_nodes[node].keys()
+         for node in total_nodes])
+    )
+
     for deb in packages_names:
         if deb in exclude_packages:
             continue
-        diff = []
-        row = []
-        for node in nodes_with_packages:
-            if not packages_versions[node]:
-                continue
-            if deb in list(packages_versions[node].keys()):
-                diff.append(packages_versions[node][deb])
-                row.append("{}: {}".format(node, packages_versions[node][deb]))
-            else:
-                row.append("{}: No package".format(node))
 
-        if diff.count(diff[0]) < len(nodes_with_packages):
-            if not is_deb_in_exception(inconsistency_rule, deb, row):
-                row.sort()
-                row.insert(0, deb)
-                packages_with_different_versions.append(row)
+        node_and_version = [
+            (node, packages_versions_by_nodes[node].get(deb, "No package"))
+            for node in total_nodes
+            if not is_deb_in_exception(inconsistency_rule, deb, node)
+        ]
+
+        if set([version for node, version in node_and_version]).__len__() > 1:
+            packages_with_different_versions[deb] = [
+                f"{node}: {version}"
+                for node, version in node_and_version]
+
     assert len(packages_with_different_versions) == 0, (
         "Non-uniform package versions are installed on '{}' group of nodes:\n"
         "{}".format(
-            group, json.dumps(packages_with_different_versions, indent=4))
+            group_name, json.dumps(packages_with_different_versions, indent=4))
     )
 
 
@@ -117,7 +128,7 @@
 @pytest.mark.full
 def test_check_module_versions(local_salt_client, nodes_in_group):
     # defines modules specific to the concrete nodes
-    inconsistency_rule = {"ctl01": ["gnocchiclient", "ujson"], "log01": ["elasticsearch"]}
+    inconsistency_rule = {"I@elasticsearch:server": ["elasticsearch"]}
     exclude_modules = utils.get_configuration().get("skipped_modules", [])
     group, nodes = nodes_in_group
     pre_check = local_salt_client.cmd(
@@ -127,8 +138,7 @@
     if list(pre_check.values()).count('') > 0:
         pytest.skip("pip is not installed on one or more nodes")
 
-    exclude_nodes = list(local_salt_client.test_ping(tgt="I@galera:master or I@gerrit:client",
-                                                expr_form='compound').keys())
+    exclude_nodes = targeted_minions("I@galera:master or I@gerrit:client")
 
     # PROD-30833
     gtw01 = local_salt_client.pillar_get(
@@ -194,7 +204,7 @@
         param='_param:updates_mirantis_login',
         expr_form='compound')
     if not restricted_repo_enabled:
-        pytest.skip("This env doesn't required the restricted ubuntu repo")
+        pytest.skip("This env doesn't require the restricted ubuntu repo")
 
     repos_by_nodes=local_salt_client.cmd(
         tgt="*",