Blame SOURCES/hvd-Add-vmbus_testing-tool-build-files.patch

8d7d92
From d8ca5e0a429b8f7395e136e713980db6a7ac8dc2 Mon Sep 17 00:00:00 2001
8d7d92
From: Mohammed Gamal <mgamal@redhat.com>
8d7d92
Date: Wed, 15 Apr 2020 12:00:30 +0200
8d7d92
Subject: [PATCH 2/2] Add vmbus_testing tool build files
8d7d92
8d7d92
RH-Author: Mohammed Gamal <mgamal@redhat.com>
8d7d92
Message-id: <20200414183955.194006-3-mgamal@redhat.com>
8d7d92
Patchwork-id: 94690
8d7d92
O-Subject: [RHEL8.3 virt hyperv-daemons PATCH v5 2/2] Add vmbus_testing tool build files
8d7d92
Bugzilla: 1816750
8d7d92
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
8d7d92
RH-Acked-by: Cathy Avery <cavery@redhat.com>
8d7d92
8d7d92
Add the vmbus_testing tool to redhat build dirs
8d7d92
8d7d92
Signed-off-by: Mohammed Gamal <mgamal@redhat.com>
8d7d92
---
8d7d92
 redhat/hyperv-daemons.spec.template |   2 +
8d7d92
 vmbus_testing                       | 376 ++++++++++++++++++++++++++++
8d7d92
 2 files changed, 378 insertions(+)
8d7d92
 create mode 100755 vmbus_testing
8d7d92
8d7d92
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
8d7d92
---
8d7d92
 redhat/hyperv-daemons.spec.template |   2 +
8d7d92
 vmbus_testing                       | 376 ++++++++++++++++++++++++++++++++++++
8d7d92
 2 files changed, 378 insertions(+)
8d7d92
 create mode 100755 vmbus_testing
8d7d92
8d7d92
diff --git a/vmbus_testing b/vmbus_testing
8d7d92
new file mode 100755
8d7d92
index 0000000..e721290
8d7d92
--- /dev/null
8d7d92
+++ b/vmbus_testing
8d7d92
@@ -0,0 +1,376 @@
8d7d92
+#!/usr/bin/env python3
8d7d92
+# SPDX-License-Identifier: GPL-2.0
8d7d92
+#
8d7d92
+# Program to allow users to fuzz test Hyper-V drivers
8d7d92
+# by interfacing with Hyper-V debugfs attributes.
8d7d92
+# Current test methods available:
8d7d92
+#       1. delay testing
8d7d92
+#
8d7d92
+# Current file/directory structure of hyper-V debugfs:
8d7d92
+#       /sys/kernel/debug/hyperv/UUID
8d7d92
+#       /sys/kernel/debug/hyperv/UUID/<test-state filename>
8d7d92
+#       /sys/kernel/debug/hyperv/UUID/<test-method sub-directory>
8d7d92
+#
8d7d92
+# author: Branden Bonaby <brandonbonaby94@gmail.com>
8d7d92
+
8d7d92
+import os
8d7d92
+import cmd
8d7d92
+import argparse
8d7d92
+import glob
8d7d92
+from argparse import RawDescriptionHelpFormatter
8d7d92
+from argparse import RawTextHelpFormatter
8d7d92
+from enum import Enum
8d7d92
+
8d7d92
+# Do not change unless, you change the debugfs attributes
8d7d92
+# in /drivers/hv/debugfs.c. All fuzz testing
8d7d92
+# attributes will start with "fuzz_test".
8d7d92
+
8d7d92
+# debugfs path for hyperv must exist before proceeding
8d7d92
+debugfs_hyperv_path = "/sys/kernel/debug/hyperv"
8d7d92
+if not os.path.isdir(debugfs_hyperv_path):
8d7d92
+        print("{} doesn't exist/check permissions".format(debugfs_hyperv_path))
8d7d92
+        exit(-1)
8d7d92
+
8d7d92
+class dev_state(Enum):
8d7d92
+        off = 0
8d7d92
+        on = 1
8d7d92
+
8d7d92
+# File names, that correspond to the files created in
8d7d92
+# /drivers/hv/debugfs.c
8d7d92
+class f_names(Enum):
8d7d92
+        state_f = "fuzz_test_state"
8d7d92
+        buff_f =  "fuzz_test_buffer_interrupt_delay"
8d7d92
+        mess_f =  "fuzz_test_message_delay"
8d7d92
+
8d7d92
+# Both single_actions and all_actions are used
8d7d92
+# for error checking and to allow for some subparser
8d7d92
+# names to be abbreviated. Do not abbreviate the
8d7d92
+# test method names, as it will become less intuitive
8d7d92
+# as to what the user can do. If you do decide to
8d7d92
+# abbreviate the test method name, make sure the main
8d7d92
+# function reflects this change.
8d7d92
+
8d7d92
+all_actions = [
8d7d92
+        "disable_all",
8d7d92
+        "D",
8d7d92
+        "enable_all",
8d7d92
+        "view_all",
8d7d92
+        "V"
8d7d92
+]
8d7d92
+
8d7d92
+single_actions = [
8d7d92
+        "disable_single",
8d7d92
+        "d",
8d7d92
+        "enable_single",
8d7d92
+        "view_single",
8d7d92
+        "v"
8d7d92
+]
8d7d92
+
8d7d92
+def main():
8d7d92
+
8d7d92
+        file_map = recursive_file_lookup(debugfs_hyperv_path, dict())
8d7d92
+        args = parse_args()
8d7d92
+        if (not args.action):
8d7d92
+                print ("Error, no options selected...exiting")
8d7d92
+                exit(-1)
8d7d92
+        arg_set = { k for (k,v) in vars(args).items() if v and k != "action" }
8d7d92
+        arg_set.add(args.action)
8d7d92
+        path = args.path if "path" in arg_set else None
8d7d92
+        if (path and path[-1] == "/"):
8d7d92
+                path = path[:-1]
8d7d92
+        validate_args_path(path, arg_set, file_map)
8d7d92
+        if (path and "enable_single" in arg_set):
8d7d92
+            state_path = locate_state(path, file_map)
8d7d92
+            set_test_state(state_path, dev_state.on.value, args.quiet)
8d7d92
+
8d7d92
+        # Use subparsers as the key for different actions
8d7d92
+        if ("delay" in arg_set):
8d7d92
+                validate_delay_values(args.delay_time)
8d7d92
+                if (args.enable_all):
8d7d92
+                        set_delay_all_devices(file_map, args.delay_time,
8d7d92
+                                              args.quiet)
8d7d92
+                else:
8d7d92
+                        set_delay_values(path, file_map, args.delay_time,
8d7d92
+                                         args.quiet)
8d7d92
+        elif ("disable_all" in arg_set or "D" in arg_set):
8d7d92
+                disable_all_testing(file_map)
8d7d92
+        elif ("disable_single" in arg_set or "d" in arg_set):
8d7d92
+                disable_testing_single_device(path, file_map)
8d7d92
+        elif ("view_all" in arg_set or "V" in arg_set):
8d7d92
+                get_all_devices_test_status(file_map)
8d7d92
+        elif ("view_single" in arg_set or  "v" in arg_set):
8d7d92
+                get_device_test_values(path, file_map)
8d7d92
+
8d7d92
+# Get the state location
8d7d92
+def locate_state(device, file_map):
8d7d92
+        return file_map[device][f_names.state_f.value]
8d7d92
+
8d7d92
+# Validate delay values to make sure they are acceptable to
8d7d92
+# enable delays on a device
8d7d92
+def validate_delay_values(delay):
8d7d92
+
8d7d92
+        if (delay[0]  == -1 and delay[1] == -1):
8d7d92
+                print("\nError, At least 1 value must be greater than 0")
8d7d92
+                exit(-1)
8d7d92
+        for i in delay:
8d7d92
+                if (i < -1 or i == 0 or i > 1000):
8d7d92
+                        print("\nError, Values must be  equal to -1 "
8d7d92
+                              "or be > 0 and <= 1000")
8d7d92
+                        exit(-1)
8d7d92
+
8d7d92
+# Validate argument path
8d7d92
+def validate_args_path(path, arg_set, file_map):
8d7d92
+
8d7d92
+        if (not path and any(element in arg_set for element in single_actions)):
8d7d92
+                print("Error, path (-p) REQUIRED for the specified option. "
8d7d92
+                      "Use (-h) to check usage.")
8d7d92
+                exit(-1)
8d7d92
+        elif (path and any(item in arg_set for item in all_actions)):
8d7d92
+                print("Error, path (-p) NOT REQUIRED for the specified option. "
8d7d92
+                      "Use (-h) to check usage." )
8d7d92
+                exit(-1)
8d7d92
+        elif (path not in file_map and any(item in arg_set
8d7d92
+                                           for item in single_actions)):
8d7d92
+                print("Error, path '{}' not a valid vmbus device".format(path))
8d7d92
+                exit(-1)
8d7d92
+
8d7d92
+# display Testing status of single device
8d7d92
+def get_device_test_values(path, file_map):
8d7d92
+
8d7d92
+        for name in file_map[path]:
8d7d92
+                file_location = file_map[path][name]
8d7d92
+                print( name + " = " + str(read_test_files(file_location)))
8d7d92
+
8d7d92
+# Create a map of the vmbus devices and their associated files
8d7d92
+# [key=device, value = [key = filename, value = file path]]
8d7d92
+def recursive_file_lookup(path, file_map):
8d7d92
+
8d7d92
+        for f_path in glob.iglob(path + '**/*'):
8d7d92
+                if (os.path.isfile(f_path)):
8d7d92
+                        if (f_path.rsplit("/",2)[0] == debugfs_hyperv_path):
8d7d92
+                                directory = f_path.rsplit("/",1)[0]
8d7d92
+                        else:
8d7d92
+                                directory = f_path.rsplit("/",2)[0]
8d7d92
+                        f_name = f_path.split("/")[-1]
8d7d92
+                        if (file_map.get(directory)):
8d7d92
+                                file_map[directory].update({f_name:f_path})
8d7d92
+                        else:
8d7d92
+                                file_map[directory] = {f_name:f_path}
8d7d92
+                elif (os.path.isdir(f_path)):
8d7d92
+                        recursive_file_lookup(f_path,file_map)
8d7d92
+        return file_map
8d7d92
+
8d7d92
+# display Testing state of devices
8d7d92
+def get_all_devices_test_status(file_map):
8d7d92
+
8d7d92
+        for device in file_map:
8d7d92
+                if (get_test_state(locate_state(device, file_map)) is 1):
8d7d92
+                        print("Testing = ON for: {}"
8d7d92
+                              .format(device.split("/")[5]))
8d7d92
+                else:
8d7d92
+                        print("Testing = OFF for: {}"
8d7d92
+                              .format(device.split("/")[5]))
8d7d92
+
8d7d92
+# read the vmbus device files, path must be absolute path before calling
8d7d92
+def read_test_files(path):
8d7d92
+        try:
8d7d92
+                with open(path,"r") as f:
8d7d92
+                        file_value = f.readline().strip()
8d7d92
+                return int(file_value)
8d7d92
+
8d7d92
+        except IOError as e:
8d7d92
+                errno, strerror = e.args
8d7d92
+                print("I/O error({0}): {1} on file {2}"
8d7d92
+                      .format(errno, strerror, path))
8d7d92
+                exit(-1)
8d7d92
+        except ValueError:
8d7d92
+                print ("Element to int conversion error in: \n{}".format(path))
8d7d92
+                exit(-1)
8d7d92
+
8d7d92
+# writing to vmbus device files, path must be absolute path before calling
8d7d92
+def write_test_files(path, value):
8d7d92
+
8d7d92
+        try:
8d7d92
+                with open(path,"w") as f:
8d7d92
+                        f.write("{}".format(value))
8d7d92
+        except IOError as e:
8d7d92
+                errno, strerror = e.args
8d7d92
+                print("I/O error({0}): {1} on file {2}"
8d7d92
+                      .format(errno, strerror, path))
8d7d92
+                exit(-1)
8d7d92
+
8d7d92
+# set testing state of device
8d7d92
+def set_test_state(state_path, state_value, quiet):
8d7d92
+
8d7d92
+        write_test_files(state_path, state_value)
8d7d92
+        if (get_test_state(state_path) is 1):
8d7d92
+                if (not quiet):
8d7d92
+                        print("Testing = ON for device: {}"
8d7d92
+                              .format(state_path.split("/")[5]))
8d7d92
+        else:
8d7d92
+                if (not quiet):
8d7d92
+                        print("Testing = OFF for device: {}"
8d7d92
+                              .format(state_path.split("/")[5]))
8d7d92
+
8d7d92
+# get testing state of device
8d7d92
+def get_test_state(state_path):
8d7d92
+        #state == 1 - test = ON
8d7d92
+        #state == 0 - test = OFF
8d7d92
+        return  read_test_files(state_path)
8d7d92
+
8d7d92
+# write 1 - 1000 microseconds, into a single device using the
8d7d92
+# fuzz_test_buffer_interrupt_delay and fuzz_test_message_delay
8d7d92
+# debugfs attributes
8d7d92
+def set_delay_values(device, file_map, delay_length, quiet):
8d7d92
+
8d7d92
+        try:
8d7d92
+                interrupt = file_map[device][f_names.buff_f.value]
8d7d92
+                message = file_map[device][f_names.mess_f.value]
8d7d92
+
8d7d92
+                # delay[0]- buffer interrupt delay, delay[1]- message delay
8d7d92
+                if (delay_length[0] >= 0 and delay_length[0] <= 1000):
8d7d92
+                        write_test_files(interrupt, delay_length[0])
8d7d92
+                if (delay_length[1] >= 0 and delay_length[1] <= 1000):
8d7d92
+                        write_test_files(message, delay_length[1])
8d7d92
+                if (not quiet):
8d7d92
+                        print("Buffer delay testing = {} for: {}"
8d7d92
+                              .format(read_test_files(interrupt),
8d7d92
+                                      interrupt.split("/")[5]))
8d7d92
+                        print("Message delay testing = {} for: {}"
8d7d92
+                              .format(read_test_files(message),
8d7d92
+                                      message.split("/")[5]))
8d7d92
+        except IOError as e:
8d7d92
+                errno, strerror = e.args
8d7d92
+                print("I/O error({0}): {1} on files {2}{3}"
8d7d92
+                      .format(errno, strerror, interrupt, message))
8d7d92
+                exit(-1)
8d7d92
+
8d7d92
+# enabling delay testing on all devices
8d7d92
+def set_delay_all_devices(file_map, delay, quiet):
8d7d92
+
8d7d92
+        for device in (file_map):
8d7d92
+                set_test_state(locate_state(device, file_map),
8d7d92
+                               dev_state.on.value,
8d7d92
+                               quiet)
8d7d92
+                set_delay_values(device, file_map, delay, quiet)
8d7d92
+
8d7d92
+# disable all testing on a SINGLE device.
8d7d92
+def disable_testing_single_device(device, file_map):
8d7d92
+
8d7d92
+        for name in file_map[device]:
8d7d92
+                file_location = file_map[device][name]
8d7d92
+                write_test_files(file_location, dev_state.off.value)
8d7d92
+        print("ALL testing now OFF for {}".format(device.split("/")[-1]))
8d7d92
+
8d7d92
+# disable all testing on ALL devices
8d7d92
+def disable_all_testing(file_map):
8d7d92
+
8d7d92
+        for device in file_map:
8d7d92
+                disable_testing_single_device(device, file_map)
8d7d92
+
8d7d92
+def parse_args():
8d7d92
+        parser = argparse.ArgumentParser(prog = "vmbus_testing",usage ="\n"
8d7d92
+                "%(prog)s [delay]   [-h] [-e|-E] -t [-p]\n"
8d7d92
+                "%(prog)s [view_all       | V]      [-h]\n"
8d7d92
+                "%(prog)s [disable_all    | D]      [-h]\n"
8d7d92
+                "%(prog)s [disable_single | d]      [-h|-p]\n"
8d7d92
+                "%(prog)s [view_single    | v]      [-h|-p]\n"
8d7d92
+                "%(prog)s --version\n",
8d7d92
+                description = "\nUse lsvmbus to get vmbus device type "
8d7d92
+                "information.\n" "\nThe debugfs root path is "
8d7d92
+                "/sys/kernel/debug/hyperv",
8d7d92
+                formatter_class = RawDescriptionHelpFormatter)
8d7d92
+        subparsers = parser.add_subparsers(dest = "action")
8d7d92
+        parser.add_argument("--version", action = "version",
8d7d92
+                version = '%(prog)s 0.1.0')
8d7d92
+        parser.add_argument("-q","--quiet", action = "store_true",
8d7d92
+                help = "silence none important test messages."
8d7d92
+                       " This will only work when enabling testing"
8d7d92
+                       " on a device.")
8d7d92
+        # Use the path parser to hold the --path attribute so it can
8d7d92
+        # be shared between subparsers. Also do the same for the state
8d7d92
+        # parser, as all testing methods will use --enable_all and
8d7d92
+        # enable_single.
8d7d92
+        path_parser = argparse.ArgumentParser(add_help=False)
8d7d92
+        path_parser.add_argument("-p","--path", metavar = "",
8d7d92
+                help = "Debugfs path to a vmbus device. The path "
8d7d92
+                "must be the absolute path to the device.")
8d7d92
+        state_parser = argparse.ArgumentParser(add_help=False)
8d7d92
+        state_group = state_parser.add_mutually_exclusive_group(required = True)
8d7d92
+        state_group.add_argument("-E", "--enable_all", action = "store_const",
8d7d92
+                                 const = "enable_all",
8d7d92
+                                 help = "Enable the specified test type "
8d7d92
+                                 "on ALL vmbus devices.")
8d7d92
+        state_group.add_argument("-e", "--enable_single",
8d7d92
+                                 action = "store_const",
8d7d92
+                                 const = "enable_single",
8d7d92
+                                 help = "Enable the specified test type on a "
8d7d92
+                                 "SINGLE vmbus device.")
8d7d92
+        parser_delay = subparsers.add_parser("delay",
8d7d92
+                        parents = [state_parser, path_parser],
8d7d92
+                        help = "Delay the ring buffer interrupt or the "
8d7d92
+                        "ring buffer message reads in microseconds.",
8d7d92
+                        prog = "vmbus_testing",
8d7d92
+                        usage = "%(prog)s [-h]\n"
8d7d92
+                        "%(prog)s -E -t [value] [value]\n"
8d7d92
+                        "%(prog)s -e -t [value] [value] -p",
8d7d92
+                        description = "Delay the ring buffer interrupt for "
8d7d92
+                        "vmbus devices, or delay the ring buffer message "
8d7d92
+                        "reads for vmbus devices (both in microseconds). This "
8d7d92
+                        "is only on the host to guest channel.")
8d7d92
+        parser_delay.add_argument("-t", "--delay_time", metavar = "", nargs = 2,
8d7d92
+                        type = check_range, default =[0,0], required = (True),
8d7d92
+                        help = "Set [buffer] & [message] delay time. "
8d7d92
+                        "Value constraints: -1 == value "
8d7d92
+                        "or 0 < value <= 1000.\n"
8d7d92
+                        "Use -1 to keep the previous value for that delay "
8d7d92
+                        "type, or a value > 0 <= 1000 to change the delay "
8d7d92
+                        "time.")
8d7d92
+        parser_dis_all = subparsers.add_parser("disable_all",
8d7d92
+                        aliases = ['D'], prog = "vmbus_testing",
8d7d92
+                        usage = "%(prog)s [disable_all | D] -h\n"
8d7d92
+                        "%(prog)s [disable_all | D]\n",
8d7d92
+                        help = "Disable ALL testing on ALL vmbus devices.",
8d7d92
+                        description = "Disable ALL testing on ALL vmbus "
8d7d92
+                        "devices.")
8d7d92
+        parser_dis_single = subparsers.add_parser("disable_single",
8d7d92
+                        aliases = ['d'],
8d7d92
+                        parents = [path_parser], prog = "vmbus_testing",
8d7d92
+                        usage = "%(prog)s [disable_single | d] -h\n"
8d7d92
+                        "%(prog)s [disable_single | d] -p\n",
8d7d92
+                        help = "Disable ALL testing on a SINGLE vmbus device.",
8d7d92
+                        description = "Disable ALL testing on a SINGLE vmbus "
8d7d92
+                        "device.")
8d7d92
+        parser_view_all = subparsers.add_parser("view_all", aliases = ['V'],
8d7d92
+                        help = "View the test state for ALL vmbus devices.",
8d7d92
+                        prog = "vmbus_testing",
8d7d92
+                        usage = "%(prog)s [view_all | V] -h\n"
8d7d92
+                        "%(prog)s [view_all | V]\n",
8d7d92
+                        description = "This shows the test state for ALL the "
8d7d92
+                        "vmbus devices.")
8d7d92
+        parser_view_single = subparsers.add_parser("view_single",
8d7d92
+                        aliases = ['v'],parents = [path_parser],
8d7d92
+                        help = "View the test values for a SINGLE vmbus "
8d7d92
+                        "device.",
8d7d92
+                        description = "This shows the test values for a SINGLE "
8d7d92
+                        "vmbus device.", prog = "vmbus_testing",
8d7d92
+                        usage = "%(prog)s [view_single | v] -h\n"
8d7d92
+                        "%(prog)s [view_single | v] -p")
8d7d92
+
8d7d92
+        return  parser.parse_args()
8d7d92
+
8d7d92
+# value checking for range checking input in parser
8d7d92
+def check_range(arg1):
8d7d92
+
8d7d92
+        try:
8d7d92
+                val = int(arg1)
8d7d92
+        except ValueError as err:
8d7d92
+                raise argparse.ArgumentTypeError(str(err))
8d7d92
+        if val < -1 or val > 1000:
8d7d92
+                message = ("\n\nvalue must be -1 or  0 < value <= 1000. "
8d7d92
+                           "Value program received: {}\n").format(val)
8d7d92
+                raise argparse.ArgumentTypeError(message)
8d7d92
+        return val
8d7d92
+
8d7d92
+if __name__ == "__main__":
8d7d92
+        main()
8d7d92
-- 
8d7d92
1.8.3.1
8d7d92