Implemented HW2HW network performance testing

Implemented HW2HW network performance test:
- Added K8S client manager and API methods
- Added method for collecting HW compute nodes
- Improved and extended global config
- Extended requirements.txt, pinned some modules
- Extended and improved SSH methods

Added some small improvements:
- extended .gitignore
- Added some custom exceptions instead of basic ones
- Renamed some classes
- Set iperf v2 to be default for multi-threads tests
- Updated README file

Related-PROD: PROD-36943
Change-Id: I265058967ccc01d96bf3bca532a8a0ae2a26f1f2
diff --git a/utils/__init__.py b/utils/__init__.py
index e4de308..155c03f 100644
--- a/utils/__init__.py
+++ b/utils/__init__.py
@@ -3,7 +3,11 @@
 import sys
 import yaml
 
+from kubernetes import client as kclient, config as kconfig
+
+from utils import exceptions
 from utils import os_client
+from utils import k8s_client
 
 logger = logging.getLogger(__name__)
 
@@ -23,8 +27,8 @@
     cmp_hosts = config.get('CMP_HOSTS') or []
     skipped_nodes = config.get('skipped_nodes') or []
     if skipped_nodes:
-        sys.stdout.write(("\nNotice: {} nodes will be skipped for vm2vm test"
-                          "".format(",".join(skipped_nodes))))
+        sys.stdout.write(("\nNotice: {} node(s) will be skipped for vm2vm "
+                          "test.\n".format(", ".join(skipped_nodes))))
         logger.info("Skipping nodes {}".format(",".join(skipped_nodes)))
     if not cmp_hosts:
         openstack_clients = os_client.OfficialClientManager(
@@ -38,13 +42,13 @@
         os_actions = os_client.OSCliActions(openstack_clients)
         nova_computes = os_actions.list_nova_computes()
         if len(nova_computes) < 2:
-            raise BaseException(
+            raise exceptions.NotEnoughNodes(
                 "At least 2 compute hosts are needed for VM2VM test, "
                 "now: {}.".format(len(nova_computes)))
         cmp_hosts = [n.host_name for n in nova_computes
                      if n.host_name not in skipped_nodes]
         if len(cmp_hosts) < 2:
-            raise BaseException(
+            raise exceptions.NotEnoughNodes(
                 "At least 2 compute hosts are needed for VM2VM test. "
                 "Cannot create a pair from {}. Please check skip list, at "
                 "least 2 computes should be tested.".format(cmp_hosts))
@@ -54,6 +58,39 @@
     return compile_pairs(cmp_hosts)
 
 
+def get_hw_pairs():
+    # get the K8S config, check whether the HW nodes list is set
+    config = get_configuration()
+    logger.info("Getting the K8S config path from the global_config.yaml file.")
+    k8s_config_path = config.get("mos_kubeconfig_path", "")
+    hw_nodes_list = config.get("hw_nodes_list", [])
+
+    # if the specific HW nodes list is not set in the config, get from K8S
+    hw_nodes = None
+    if not hw_nodes_list:
+        # fetch only compute nodes
+        label_selector = "openstack-compute-node=enabled," \
+                         "openvswitch=enabled"
+        k8s_api = k8s_client.K8SClientManager(k8s_config_path=k8s_config_path)
+        k8s_actions = k8s_client.K8SCliActions(k8s_api.k8s_v1)
+        hw_nodes_list = k8s_actions.list_nodes_names(
+            label_selector=label_selector)
+
+    # remove some skipped nodes if any
+    skipped_nodes = config.get('skipped_nodes', [])
+    if skipped_nodes:
+        print(f"Notice: {', '.join(skipped_nodes)} node(s) will be skipped for"
+              f" hw2hw test.\n")
+    hw_nodes = [node for node in hw_nodes_list
+                if node not in skipped_nodes]
+    if len(hw_nodes) < 2:
+        raise exceptions.NotEnoughNodes(
+            f"At least 2 HW nodes are required to run hw2hw test. Cannot "
+            f"create a pair from {hw_nodes}. Check whether the cluster has at"
+            f" least 2 compute nodes, or the nodes are not in the skip list.")
+    return compile_pairs(hw_nodes)
+
+
 def get_configuration():
     """function returns configuration for environment
     and for test if it's specified"""
@@ -77,8 +114,9 @@
 def check_iperf_utility(actual_iperf_utility):
     valid_values = ["iperf", "iperf3"]
     if actual_iperf_utility not in valid_values:
-        raise BaseException("The iperf utility for multiple threads test case "
-                            "is not correct. Valid value is one of {}. Actual "
-                            "value is {}. Please set the correct value in "
-                            "global_config.yaml:multiple_threads_iperf_utility"
-                            "".format(valid_values, actual_iperf_utility))
+        raise exceptions.InvalidConfigException(
+            "The iperf utility for multiple threads test case is not correct. "
+            "Valid value is one of {}. Actual value is {}. Please set the "
+            "correct value in global_config.yaml:"
+            "multiple_threads_iperf_utility".format(
+                valid_values, actual_iperf_utility))