CSIT-619 HC Test: Honeycomb performance testing - initial commit
[csit.git] / resources / libraries / python / honeycomb / Performance.py
diff --git a/resources/libraries/python/honeycomb/Performance.py b/resources/libraries/python/honeycomb/Performance.py
new file mode 100644 (file)
index 0000000..1c6b0bc
--- /dev/null
@@ -0,0 +1,129 @@
+# Copyright (c) 2017 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Implementation of keywords for testing Honeycomb performance."""
+
+from resources.libraries.python.ssh import SSH
+from resources.libraries.python.constants import Constants as Const
+from resources.libraries.python.honeycomb.HoneycombUtil import HoneycombError
+
+
+class Performance(object):
+    """Keywords used in Honeycomb performance testing."""
+
+    def __init__(self):
+        """Initializer."""
+        pass
+
+    @staticmethod
+    def configure_netconf_threads(node, threads):
+        """Set Honeycomb's Netconf thread count in configuration.
+
+        :param node: Honeycomb node.
+        :param threads: Number of threads.
+        :type node: dict
+        :type threads: int
+        :raises HoneycombError: If the operation fails.
+        """
+
+        find = "netconf-netty-threads"
+        replace = '\\"netconf-netty-threads\\": {0},'.format(threads)
+
+        argument = '"/{0}/c\\ {1}"'.format(find, replace)
+        path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR)
+        command = "sed -i {0} {1}".format(argument, path)
+
+        ssh = SSH()
+        ssh.connect(node)
+        (ret_code, _, stderr) = ssh.exec_command_sudo(command)
+        if ret_code != 0:
+            raise HoneycombError("Failed to modify configuration on "
+                                 "node {0}, {1}".format(node, stderr))
+
+    @staticmethod
+    def run_traffic_script_on_dut(node, script, cores, reserved=2,
+                                  *args, **kwargs):
+        """Copy traffic script over to the specified node and execute with
+        the provided arguments.
+
+        :param node: Node in topology.
+        :param script: Name of the script to execute.
+        :param cores: Number of processor cores to use.
+        :param reserved: Number of cores reserved for other tasks. Default is 2,
+        one for system tasks and one for VPP main thread.
+        :param args: Sequential arguments for the script.
+        :param kwargs: Named arguments for the script.
+        :type node: dict
+        :type script: str
+        :type cores: int
+        :type reserved: int
+        :type args: list
+        :type kwargs: dict
+        """
+
+        path = "resources/traffic_scripts/honeycomb/{0}".format(script)
+
+        # Assemble arguments for traffic script
+        arguments = ""
+        for arg in args:
+            arguments += "{0} ".format(arg)
+
+        for key, value in kwargs.items():
+            arguments += "--{0} {1} ".format(key, value)
+
+        ssh = SSH()
+        ssh.connect(node)
+        ssh.scp(path, "/tmp")
+
+        # Use alternate scheduler, Ubuntu's default can't load-balance
+        # over isolcpus
+        scheduler = "chrt -f 99"
+        core_afi = "taskset -c {first}-{last}".format(
+            first=reserved, last=cores-1)
+
+        cmd = "{scheduler} {affinity} python /tmp/{script} {args}".format(
+            scheduler=scheduler,
+            affinity=core_afi,
+            script=script,
+            args=arguments)
+
+        ret_code, stdout, _ = ssh.exec_command_sudo(cmd, timeout=600)
+
+        ssh.exec_command("sudo pkill python ; rm /tmp/{0}".format(script))
+        if ret_code != 0:
+            raise HoneycombError("Traffic script failed to execute.")
+        for line in stdout.splitlines():
+            if "Avg. requests" in line:
+                return line
+
+    @staticmethod
+    def log_core_schedule(node, process):
+        """Determine which cores the process' threads are running on.
+
+        :param node: Honeycomb node.
+        :param process: Name of the process.
+        :type node: dict
+        :type process: str
+        """
+
+        # Get info on process and all of its children
+        cmd1 = """cat /proc/`pidof {0}`/task/*/stat""".format(process)
+
+        # Parse process ID, name and core index
+        cmd2 = """awk '{print $1" "$2" "$39}'"""
+
+        cmd = "{0} | {1}".format(cmd1, cmd2)
+
+        ssh = SSH()
+        ssh.connect(node)
+        ssh.exec_command(cmd)