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/ssh.py b/utils/ssh.py
index e9e5f3a..caca30e 100644
--- a/utils/ssh.py
+++ b/utils/ssh.py
@@ -6,6 +6,8 @@
 import time
 import os
 
+from netaddr import IPNetwork, IPAddress
+
 logger = logging.getLogger(__name__)
 
 # Suppress paramiko logging
@@ -20,6 +22,10 @@
         self.username = username
         self.password = password
         if private_key is not None:
+            if os.path.isfile(private_key):
+                with open(private_key, 'r') as key_file:
+                    private_key_content = key_file.read()
+                private_key = private_key_content
             self.private_key = paramiko.RSAKey.from_private_key(
                 StringIO(private_key))
         else:
@@ -131,7 +137,7 @@
                            any(req in pack for req in required_packages) if
                            pack.endswith('.deb')]
         if not iperf_deb_files:
-            raise BaseException(
+            raise utils.exceptions.NoPackageInstalled(
                 "iperf3 or iperf *.deb packages are not found locally at path"
                 " {}. Please recheck 'iperf_deb_package_dir_path' variable in "
                 "global_config.yaml and check *.deb packages are manually "
@@ -178,7 +184,8 @@
                             "seconds.".format(floating_ip, attempts, bsleep))
                 time.sleep(bsleep)
 
-    def get_mtu_from_vm(self, floating_ip, user='ubuntu', password='password',
+    @staticmethod
+    def get_mtu_from_vm(floating_ip, user='ubuntu', password='password',
                         private_key=None):
         transport = SSHTransport(floating_ip, user, password, private_key)
         iface = (transport.exec_command(
@@ -186,8 +193,29 @@
         mtu = transport.exec_command('cat /sys/class/net/{}/mtu'.format(iface))
         return mtu.decode("utf-8")
 
+    @staticmethod
+    def get_node_ip_addresses_from_cidr(ip, cidr, user='mcc-user',
+                                        private_key=None):
+        transport = SSHTransport(
+            ip, username=user, private_key=private_key)
+        command = "ip -4 addr show scope global"
+        output = transport.exec_command(command)
+        try:
+            all_ip_addresses = [line.split()[1] for line in
+                                output.decode().splitlines()
+                                if "/" in line.split()[1]]
+            for address in all_ip_addresses:
+                ip_addr = address.split('/')[0]
+                if IPAddress(ip_addr) in IPNetwork(cidr):
+                    return ip_addr
+        except Exception as e:
+            raise utils.exceptions.InvalidConfigException(
+                f"Could not find the IP at the interface {cidr} at the node "
+                f"with K8S Private IP {ip}. Please check the configuration. "
+                f"\nException: {e}")
 
-class prepare_iperf(object):
+
+class IperfAtVM(object):
 
     def __init__(self, fip, user='ubuntu', password='password',
                  private_key=None):
@@ -218,13 +246,49 @@
         logger.info(check.decode('utf-8'))
         if not check:
             if internet_at_vms.lower() == 'true':
-                info = "Please check the Internet access at VM."
+                info = "Please check the Internet access at VM"
             else:
                 info = "Could not put offline iperf packages from {} to the " \
-                       "VM.".format(path_to_iperf_deb)
-            raise BaseException("iperf3 is not installed at VM with FIP {}. "
-                                "{}.\nStdout, stderr at VM:\n{}\n{}"
-                                "".format(fip, info, stdout, stderr))
+                       "VM".format(path_to_iperf_deb)
+            raise utils.exceptions.NoPackageInstalled(
+                "iperf3 is not installed at VM with FIP {}. {}.\nStdout, "
+                "stderr at VM:\n{}\n{}".format(fip, info, stdout, stderr))
         # Staring iperf server
         transport.exec_command('nohup iperf3 -s > file 2>&1 &')
         transport.exec_command('nohup iperf -s > file 2>&1 &')
+
+
+class IperfAtNode(object):
+
+    def __init__(self, ip, iperf_test_ip, user='mcc-user', private_key=None):
+        transport = SSHTransport(ip, user, private_key=private_key)
+        # TODO: to avoid looping, both packages can be installed by one
+        #  'install -y iperf iperf3' command. 'which' command can also be
+        #  executed for multiple packages at once.
+        packages = ["iperf", "iperf3"]
+        for p in packages:
+            check_path = transport.exec_command(f'which {p}')
+            if not check_path:
+                self.install_iperf = True
+                # Install iperf/iperf3 at MOSK nodes
+                logger.info(f"Installing {p} at MOSK nodes...")
+                _, stdout, stderr = transport.exec_sync(
+                    f"sudo apt update && sudo apt install -y {p}")
+            else:
+                self.install_iperf = False
+            check_path = transport.exec_command(f'which {p}')
+            # Log whether iperf is installed
+            logger.info(f"{p} package path: {check_path.decode('utf-8')}")
+            if not check_path:
+                raise utils.exceptions.NoPackageInstalled(
+                    f"{p} is not installed at the MOSK node with IP {ip}.\n"
+                    f"Stdout, stderr at VM:\n{stdout}\n{stderr}")
+            # Staring iperf/iperf3 server
+            transport.exec_command(
+                f"nohup {p} -s -B {iperf_test_ip} > file 2>&1 &")
+
+    @staticmethod
+    def remove_iperf_packages(ip, user='mcc-user', private_key=None):
+        transport = SSHTransport(ip, user, private_key=private_key)
+        logger.info(f"Removing iperf,iperf3 packages from the node {ip}...")
+        transport.exec_command("sudo apt remove iperf iperf3 -y")