1*4882a593Smuzhiyun#!/usr/bin/env python3 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# Program to allow users to fuzz test Hyper-V drivers 5*4882a593Smuzhiyun# by interfacing with Hyper-V debugfs attributes. 6*4882a593Smuzhiyun# Current test methods available: 7*4882a593Smuzhiyun# 1. delay testing 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun# Current file/directory structure of hyper-V debugfs: 10*4882a593Smuzhiyun# /sys/kernel/debug/hyperv/UUID 11*4882a593Smuzhiyun# /sys/kernel/debug/hyperv/UUID/<test-state filename> 12*4882a593Smuzhiyun# /sys/kernel/debug/hyperv/UUID/<test-method sub-directory> 13*4882a593Smuzhiyun# 14*4882a593Smuzhiyun# author: Branden Bonaby <brandonbonaby94@gmail.com> 15*4882a593Smuzhiyun 16*4882a593Smuzhiyunimport os 17*4882a593Smuzhiyunimport cmd 18*4882a593Smuzhiyunimport argparse 19*4882a593Smuzhiyunimport glob 20*4882a593Smuzhiyunfrom argparse import RawDescriptionHelpFormatter 21*4882a593Smuzhiyunfrom argparse import RawTextHelpFormatter 22*4882a593Smuzhiyunfrom enum import Enum 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun# Do not change unless, you change the debugfs attributes 25*4882a593Smuzhiyun# in /drivers/hv/debugfs.c. All fuzz testing 26*4882a593Smuzhiyun# attributes will start with "fuzz_test". 27*4882a593Smuzhiyun 28*4882a593Smuzhiyun# debugfs path for hyperv must exist before proceeding 29*4882a593Smuzhiyundebugfs_hyperv_path = "/sys/kernel/debug/hyperv" 30*4882a593Smuzhiyunif not os.path.isdir(debugfs_hyperv_path): 31*4882a593Smuzhiyun print("{} doesn't exist/check permissions".format(debugfs_hyperv_path)) 32*4882a593Smuzhiyun exit(-1) 33*4882a593Smuzhiyun 34*4882a593Smuzhiyunclass dev_state(Enum): 35*4882a593Smuzhiyun off = 0 36*4882a593Smuzhiyun on = 1 37*4882a593Smuzhiyun 38*4882a593Smuzhiyun# File names, that correspond to the files created in 39*4882a593Smuzhiyun# /drivers/hv/debugfs.c 40*4882a593Smuzhiyunclass f_names(Enum): 41*4882a593Smuzhiyun state_f = "fuzz_test_state" 42*4882a593Smuzhiyun buff_f = "fuzz_test_buffer_interrupt_delay" 43*4882a593Smuzhiyun mess_f = "fuzz_test_message_delay" 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun# Both single_actions and all_actions are used 46*4882a593Smuzhiyun# for error checking and to allow for some subparser 47*4882a593Smuzhiyun# names to be abbreviated. Do not abbreviate the 48*4882a593Smuzhiyun# test method names, as it will become less intuitive 49*4882a593Smuzhiyun# as to what the user can do. If you do decide to 50*4882a593Smuzhiyun# abbreviate the test method name, make sure the main 51*4882a593Smuzhiyun# function reflects this change. 52*4882a593Smuzhiyun 53*4882a593Smuzhiyunall_actions = [ 54*4882a593Smuzhiyun "disable_all", 55*4882a593Smuzhiyun "D", 56*4882a593Smuzhiyun "enable_all", 57*4882a593Smuzhiyun "view_all", 58*4882a593Smuzhiyun "V" 59*4882a593Smuzhiyun] 60*4882a593Smuzhiyun 61*4882a593Smuzhiyunsingle_actions = [ 62*4882a593Smuzhiyun "disable_single", 63*4882a593Smuzhiyun "d", 64*4882a593Smuzhiyun "enable_single", 65*4882a593Smuzhiyun "view_single", 66*4882a593Smuzhiyun "v" 67*4882a593Smuzhiyun] 68*4882a593Smuzhiyun 69*4882a593Smuzhiyundef main(): 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun file_map = recursive_file_lookup(debugfs_hyperv_path, dict()) 72*4882a593Smuzhiyun args = parse_args() 73*4882a593Smuzhiyun if (not args.action): 74*4882a593Smuzhiyun print ("Error, no options selected...exiting") 75*4882a593Smuzhiyun exit(-1) 76*4882a593Smuzhiyun arg_set = { k for (k,v) in vars(args).items() if v and k != "action" } 77*4882a593Smuzhiyun arg_set.add(args.action) 78*4882a593Smuzhiyun path = args.path if "path" in arg_set else None 79*4882a593Smuzhiyun if (path and path[-1] == "/"): 80*4882a593Smuzhiyun path = path[:-1] 81*4882a593Smuzhiyun validate_args_path(path, arg_set, file_map) 82*4882a593Smuzhiyun if (path and "enable_single" in arg_set): 83*4882a593Smuzhiyun state_path = locate_state(path, file_map) 84*4882a593Smuzhiyun set_test_state(state_path, dev_state.on.value, args.quiet) 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun # Use subparsers as the key for different actions 87*4882a593Smuzhiyun if ("delay" in arg_set): 88*4882a593Smuzhiyun validate_delay_values(args.delay_time) 89*4882a593Smuzhiyun if (args.enable_all): 90*4882a593Smuzhiyun set_delay_all_devices(file_map, args.delay_time, 91*4882a593Smuzhiyun args.quiet) 92*4882a593Smuzhiyun else: 93*4882a593Smuzhiyun set_delay_values(path, file_map, args.delay_time, 94*4882a593Smuzhiyun args.quiet) 95*4882a593Smuzhiyun elif ("disable_all" in arg_set or "D" in arg_set): 96*4882a593Smuzhiyun disable_all_testing(file_map) 97*4882a593Smuzhiyun elif ("disable_single" in arg_set or "d" in arg_set): 98*4882a593Smuzhiyun disable_testing_single_device(path, file_map) 99*4882a593Smuzhiyun elif ("view_all" in arg_set or "V" in arg_set): 100*4882a593Smuzhiyun get_all_devices_test_status(file_map) 101*4882a593Smuzhiyun elif ("view_single" in arg_set or "v" in arg_set): 102*4882a593Smuzhiyun get_device_test_values(path, file_map) 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun# Get the state location 105*4882a593Smuzhiyundef locate_state(device, file_map): 106*4882a593Smuzhiyun return file_map[device][f_names.state_f.value] 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun# Validate delay values to make sure they are acceptable to 109*4882a593Smuzhiyun# enable delays on a device 110*4882a593Smuzhiyundef validate_delay_values(delay): 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun if (delay[0] == -1 and delay[1] == -1): 113*4882a593Smuzhiyun print("\nError, At least 1 value must be greater than 0") 114*4882a593Smuzhiyun exit(-1) 115*4882a593Smuzhiyun for i in delay: 116*4882a593Smuzhiyun if (i < -1 or i == 0 or i > 1000): 117*4882a593Smuzhiyun print("\nError, Values must be equal to -1 " 118*4882a593Smuzhiyun "or be > 0 and <= 1000") 119*4882a593Smuzhiyun exit(-1) 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun# Validate argument path 122*4882a593Smuzhiyundef validate_args_path(path, arg_set, file_map): 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun if (not path and any(element in arg_set for element in single_actions)): 125*4882a593Smuzhiyun print("Error, path (-p) REQUIRED for the specified option. " 126*4882a593Smuzhiyun "Use (-h) to check usage.") 127*4882a593Smuzhiyun exit(-1) 128*4882a593Smuzhiyun elif (path and any(item in arg_set for item in all_actions)): 129*4882a593Smuzhiyun print("Error, path (-p) NOT REQUIRED for the specified option. " 130*4882a593Smuzhiyun "Use (-h) to check usage." ) 131*4882a593Smuzhiyun exit(-1) 132*4882a593Smuzhiyun elif (path not in file_map and any(item in arg_set 133*4882a593Smuzhiyun for item in single_actions)): 134*4882a593Smuzhiyun print("Error, path '{}' not a valid vmbus device".format(path)) 135*4882a593Smuzhiyun exit(-1) 136*4882a593Smuzhiyun 137*4882a593Smuzhiyun# display Testing status of single device 138*4882a593Smuzhiyundef get_device_test_values(path, file_map): 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun for name in file_map[path]: 141*4882a593Smuzhiyun file_location = file_map[path][name] 142*4882a593Smuzhiyun print( name + " = " + str(read_test_files(file_location))) 143*4882a593Smuzhiyun 144*4882a593Smuzhiyun# Create a map of the vmbus devices and their associated files 145*4882a593Smuzhiyun# [key=device, value = [key = filename, value = file path]] 146*4882a593Smuzhiyundef recursive_file_lookup(path, file_map): 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun for f_path in glob.iglob(path + '**/*'): 149*4882a593Smuzhiyun if (os.path.isfile(f_path)): 150*4882a593Smuzhiyun if (f_path.rsplit("/",2)[0] == debugfs_hyperv_path): 151*4882a593Smuzhiyun directory = f_path.rsplit("/",1)[0] 152*4882a593Smuzhiyun else: 153*4882a593Smuzhiyun directory = f_path.rsplit("/",2)[0] 154*4882a593Smuzhiyun f_name = f_path.split("/")[-1] 155*4882a593Smuzhiyun if (file_map.get(directory)): 156*4882a593Smuzhiyun file_map[directory].update({f_name:f_path}) 157*4882a593Smuzhiyun else: 158*4882a593Smuzhiyun file_map[directory] = {f_name:f_path} 159*4882a593Smuzhiyun elif (os.path.isdir(f_path)): 160*4882a593Smuzhiyun recursive_file_lookup(f_path,file_map) 161*4882a593Smuzhiyun return file_map 162*4882a593Smuzhiyun 163*4882a593Smuzhiyun# display Testing state of devices 164*4882a593Smuzhiyundef get_all_devices_test_status(file_map): 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun for device in file_map: 167*4882a593Smuzhiyun if (get_test_state(locate_state(device, file_map)) is 1): 168*4882a593Smuzhiyun print("Testing = ON for: {}" 169*4882a593Smuzhiyun .format(device.split("/")[5])) 170*4882a593Smuzhiyun else: 171*4882a593Smuzhiyun print("Testing = OFF for: {}" 172*4882a593Smuzhiyun .format(device.split("/")[5])) 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun# read the vmbus device files, path must be absolute path before calling 175*4882a593Smuzhiyundef read_test_files(path): 176*4882a593Smuzhiyun try: 177*4882a593Smuzhiyun with open(path,"r") as f: 178*4882a593Smuzhiyun file_value = f.readline().strip() 179*4882a593Smuzhiyun return int(file_value) 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun except IOError as e: 182*4882a593Smuzhiyun errno, strerror = e.args 183*4882a593Smuzhiyun print("I/O error({0}): {1} on file {2}" 184*4882a593Smuzhiyun .format(errno, strerror, path)) 185*4882a593Smuzhiyun exit(-1) 186*4882a593Smuzhiyun except ValueError: 187*4882a593Smuzhiyun print ("Element to int conversion error in: \n{}".format(path)) 188*4882a593Smuzhiyun exit(-1) 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun# writing to vmbus device files, path must be absolute path before calling 191*4882a593Smuzhiyundef write_test_files(path, value): 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun try: 194*4882a593Smuzhiyun with open(path,"w") as f: 195*4882a593Smuzhiyun f.write("{}".format(value)) 196*4882a593Smuzhiyun except IOError as e: 197*4882a593Smuzhiyun errno, strerror = e.args 198*4882a593Smuzhiyun print("I/O error({0}): {1} on file {2}" 199*4882a593Smuzhiyun .format(errno, strerror, path)) 200*4882a593Smuzhiyun exit(-1) 201*4882a593Smuzhiyun 202*4882a593Smuzhiyun# set testing state of device 203*4882a593Smuzhiyundef set_test_state(state_path, state_value, quiet): 204*4882a593Smuzhiyun 205*4882a593Smuzhiyun write_test_files(state_path, state_value) 206*4882a593Smuzhiyun if (get_test_state(state_path) is 1): 207*4882a593Smuzhiyun if (not quiet): 208*4882a593Smuzhiyun print("Testing = ON for device: {}" 209*4882a593Smuzhiyun .format(state_path.split("/")[5])) 210*4882a593Smuzhiyun else: 211*4882a593Smuzhiyun if (not quiet): 212*4882a593Smuzhiyun print("Testing = OFF for device: {}" 213*4882a593Smuzhiyun .format(state_path.split("/")[5])) 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun# get testing state of device 216*4882a593Smuzhiyundef get_test_state(state_path): 217*4882a593Smuzhiyun #state == 1 - test = ON 218*4882a593Smuzhiyun #state == 0 - test = OFF 219*4882a593Smuzhiyun return read_test_files(state_path) 220*4882a593Smuzhiyun 221*4882a593Smuzhiyun# write 1 - 1000 microseconds, into a single device using the 222*4882a593Smuzhiyun# fuzz_test_buffer_interrupt_delay and fuzz_test_message_delay 223*4882a593Smuzhiyun# debugfs attributes 224*4882a593Smuzhiyundef set_delay_values(device, file_map, delay_length, quiet): 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun try: 227*4882a593Smuzhiyun interrupt = file_map[device][f_names.buff_f.value] 228*4882a593Smuzhiyun message = file_map[device][f_names.mess_f.value] 229*4882a593Smuzhiyun 230*4882a593Smuzhiyun # delay[0]- buffer interrupt delay, delay[1]- message delay 231*4882a593Smuzhiyun if (delay_length[0] >= 0 and delay_length[0] <= 1000): 232*4882a593Smuzhiyun write_test_files(interrupt, delay_length[0]) 233*4882a593Smuzhiyun if (delay_length[1] >= 0 and delay_length[1] <= 1000): 234*4882a593Smuzhiyun write_test_files(message, delay_length[1]) 235*4882a593Smuzhiyun if (not quiet): 236*4882a593Smuzhiyun print("Buffer delay testing = {} for: {}" 237*4882a593Smuzhiyun .format(read_test_files(interrupt), 238*4882a593Smuzhiyun interrupt.split("/")[5])) 239*4882a593Smuzhiyun print("Message delay testing = {} for: {}" 240*4882a593Smuzhiyun .format(read_test_files(message), 241*4882a593Smuzhiyun message.split("/")[5])) 242*4882a593Smuzhiyun except IOError as e: 243*4882a593Smuzhiyun errno, strerror = e.args 244*4882a593Smuzhiyun print("I/O error({0}): {1} on files {2}{3}" 245*4882a593Smuzhiyun .format(errno, strerror, interrupt, message)) 246*4882a593Smuzhiyun exit(-1) 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun# enabling delay testing on all devices 249*4882a593Smuzhiyundef set_delay_all_devices(file_map, delay, quiet): 250*4882a593Smuzhiyun 251*4882a593Smuzhiyun for device in (file_map): 252*4882a593Smuzhiyun set_test_state(locate_state(device, file_map), 253*4882a593Smuzhiyun dev_state.on.value, 254*4882a593Smuzhiyun quiet) 255*4882a593Smuzhiyun set_delay_values(device, file_map, delay, quiet) 256*4882a593Smuzhiyun 257*4882a593Smuzhiyun# disable all testing on a SINGLE device. 258*4882a593Smuzhiyundef disable_testing_single_device(device, file_map): 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun for name in file_map[device]: 261*4882a593Smuzhiyun file_location = file_map[device][name] 262*4882a593Smuzhiyun write_test_files(file_location, dev_state.off.value) 263*4882a593Smuzhiyun print("ALL testing now OFF for {}".format(device.split("/")[-1])) 264*4882a593Smuzhiyun 265*4882a593Smuzhiyun# disable all testing on ALL devices 266*4882a593Smuzhiyundef disable_all_testing(file_map): 267*4882a593Smuzhiyun 268*4882a593Smuzhiyun for device in file_map: 269*4882a593Smuzhiyun disable_testing_single_device(device, file_map) 270*4882a593Smuzhiyun 271*4882a593Smuzhiyundef parse_args(): 272*4882a593Smuzhiyun parser = argparse.ArgumentParser(prog = "vmbus_testing",usage ="\n" 273*4882a593Smuzhiyun "%(prog)s [delay] [-h] [-e|-E] -t [-p]\n" 274*4882a593Smuzhiyun "%(prog)s [view_all | V] [-h]\n" 275*4882a593Smuzhiyun "%(prog)s [disable_all | D] [-h]\n" 276*4882a593Smuzhiyun "%(prog)s [disable_single | d] [-h|-p]\n" 277*4882a593Smuzhiyun "%(prog)s [view_single | v] [-h|-p]\n" 278*4882a593Smuzhiyun "%(prog)s --version\n", 279*4882a593Smuzhiyun description = "\nUse lsvmbus to get vmbus device type " 280*4882a593Smuzhiyun "information.\n" "\nThe debugfs root path is " 281*4882a593Smuzhiyun "/sys/kernel/debug/hyperv", 282*4882a593Smuzhiyun formatter_class = RawDescriptionHelpFormatter) 283*4882a593Smuzhiyun subparsers = parser.add_subparsers(dest = "action") 284*4882a593Smuzhiyun parser.add_argument("--version", action = "version", 285*4882a593Smuzhiyun version = '%(prog)s 0.1.0') 286*4882a593Smuzhiyun parser.add_argument("-q","--quiet", action = "store_true", 287*4882a593Smuzhiyun help = "silence none important test messages." 288*4882a593Smuzhiyun " This will only work when enabling testing" 289*4882a593Smuzhiyun " on a device.") 290*4882a593Smuzhiyun # Use the path parser to hold the --path attribute so it can 291*4882a593Smuzhiyun # be shared between subparsers. Also do the same for the state 292*4882a593Smuzhiyun # parser, as all testing methods will use --enable_all and 293*4882a593Smuzhiyun # enable_single. 294*4882a593Smuzhiyun path_parser = argparse.ArgumentParser(add_help=False) 295*4882a593Smuzhiyun path_parser.add_argument("-p","--path", metavar = "", 296*4882a593Smuzhiyun help = "Debugfs path to a vmbus device. The path " 297*4882a593Smuzhiyun "must be the absolute path to the device.") 298*4882a593Smuzhiyun state_parser = argparse.ArgumentParser(add_help=False) 299*4882a593Smuzhiyun state_group = state_parser.add_mutually_exclusive_group(required = True) 300*4882a593Smuzhiyun state_group.add_argument("-E", "--enable_all", action = "store_const", 301*4882a593Smuzhiyun const = "enable_all", 302*4882a593Smuzhiyun help = "Enable the specified test type " 303*4882a593Smuzhiyun "on ALL vmbus devices.") 304*4882a593Smuzhiyun state_group.add_argument("-e", "--enable_single", 305*4882a593Smuzhiyun action = "store_const", 306*4882a593Smuzhiyun const = "enable_single", 307*4882a593Smuzhiyun help = "Enable the specified test type on a " 308*4882a593Smuzhiyun "SINGLE vmbus device.") 309*4882a593Smuzhiyun parser_delay = subparsers.add_parser("delay", 310*4882a593Smuzhiyun parents = [state_parser, path_parser], 311*4882a593Smuzhiyun help = "Delay the ring buffer interrupt or the " 312*4882a593Smuzhiyun "ring buffer message reads in microseconds.", 313*4882a593Smuzhiyun prog = "vmbus_testing", 314*4882a593Smuzhiyun usage = "%(prog)s [-h]\n" 315*4882a593Smuzhiyun "%(prog)s -E -t [value] [value]\n" 316*4882a593Smuzhiyun "%(prog)s -e -t [value] [value] -p", 317*4882a593Smuzhiyun description = "Delay the ring buffer interrupt for " 318*4882a593Smuzhiyun "vmbus devices, or delay the ring buffer message " 319*4882a593Smuzhiyun "reads for vmbus devices (both in microseconds). This " 320*4882a593Smuzhiyun "is only on the host to guest channel.") 321*4882a593Smuzhiyun parser_delay.add_argument("-t", "--delay_time", metavar = "", nargs = 2, 322*4882a593Smuzhiyun type = check_range, default =[0,0], required = (True), 323*4882a593Smuzhiyun help = "Set [buffer] & [message] delay time. " 324*4882a593Smuzhiyun "Value constraints: -1 == value " 325*4882a593Smuzhiyun "or 0 < value <= 1000.\n" 326*4882a593Smuzhiyun "Use -1 to keep the previous value for that delay " 327*4882a593Smuzhiyun "type, or a value > 0 <= 1000 to change the delay " 328*4882a593Smuzhiyun "time.") 329*4882a593Smuzhiyun parser_dis_all = subparsers.add_parser("disable_all", 330*4882a593Smuzhiyun aliases = ['D'], prog = "vmbus_testing", 331*4882a593Smuzhiyun usage = "%(prog)s [disable_all | D] -h\n" 332*4882a593Smuzhiyun "%(prog)s [disable_all | D]\n", 333*4882a593Smuzhiyun help = "Disable ALL testing on ALL vmbus devices.", 334*4882a593Smuzhiyun description = "Disable ALL testing on ALL vmbus " 335*4882a593Smuzhiyun "devices.") 336*4882a593Smuzhiyun parser_dis_single = subparsers.add_parser("disable_single", 337*4882a593Smuzhiyun aliases = ['d'], 338*4882a593Smuzhiyun parents = [path_parser], prog = "vmbus_testing", 339*4882a593Smuzhiyun usage = "%(prog)s [disable_single | d] -h\n" 340*4882a593Smuzhiyun "%(prog)s [disable_single | d] -p\n", 341*4882a593Smuzhiyun help = "Disable ALL testing on a SINGLE vmbus device.", 342*4882a593Smuzhiyun description = "Disable ALL testing on a SINGLE vmbus " 343*4882a593Smuzhiyun "device.") 344*4882a593Smuzhiyun parser_view_all = subparsers.add_parser("view_all", aliases = ['V'], 345*4882a593Smuzhiyun help = "View the test state for ALL vmbus devices.", 346*4882a593Smuzhiyun prog = "vmbus_testing", 347*4882a593Smuzhiyun usage = "%(prog)s [view_all | V] -h\n" 348*4882a593Smuzhiyun "%(prog)s [view_all | V]\n", 349*4882a593Smuzhiyun description = "This shows the test state for ALL the " 350*4882a593Smuzhiyun "vmbus devices.") 351*4882a593Smuzhiyun parser_view_single = subparsers.add_parser("view_single", 352*4882a593Smuzhiyun aliases = ['v'],parents = [path_parser], 353*4882a593Smuzhiyun help = "View the test values for a SINGLE vmbus " 354*4882a593Smuzhiyun "device.", 355*4882a593Smuzhiyun description = "This shows the test values for a SINGLE " 356*4882a593Smuzhiyun "vmbus device.", prog = "vmbus_testing", 357*4882a593Smuzhiyun usage = "%(prog)s [view_single | v] -h\n" 358*4882a593Smuzhiyun "%(prog)s [view_single | v] -p") 359*4882a593Smuzhiyun 360*4882a593Smuzhiyun return parser.parse_args() 361*4882a593Smuzhiyun 362*4882a593Smuzhiyun# value checking for range checking input in parser 363*4882a593Smuzhiyundef check_range(arg1): 364*4882a593Smuzhiyun 365*4882a593Smuzhiyun try: 366*4882a593Smuzhiyun val = int(arg1) 367*4882a593Smuzhiyun except ValueError as err: 368*4882a593Smuzhiyun raise argparse.ArgumentTypeError(str(err)) 369*4882a593Smuzhiyun if val < -1 or val > 1000: 370*4882a593Smuzhiyun message = ("\n\nvalue must be -1 or 0 < value <= 1000. " 371*4882a593Smuzhiyun "Value program received: {}\n").format(val) 372*4882a593Smuzhiyun raise argparse.ArgumentTypeError(message) 373*4882a593Smuzhiyun return val 374*4882a593Smuzhiyun 375*4882a593Smuzhiyunif __name__ == "__main__": 376*4882a593Smuzhiyun main() 377