import sys
import os,time
# print("###"+os.path.abspath(os.path.dirname(os.path.dirname(os.getcwd()))))
sys.path.append(os.path.abspath(os.path.dirname(os.path.dirname(os.getcwd())))) 

from loguru import logger
from pywebio.input import input, FLOAT,NUMBER,input_group,select, textarea,file_upload
from pywebio.output import close_popup, output, put_file, put_html, put_image, put_markdown, put_text,popup,put_link,put_code
from pywebio import start_server
from core.bladeTest.main import RemoteRunner,generateHtmlReport,running
import json
from core.xmind2excel import makeCase
from core.utils import swagger2jmeter


def app():
    select_type = select("选择你要做的操作:",["xmind转excel","混沌测试-交互式","混沌测试-直接输入(推荐)","swagger地址转换jmeter脚本"])

    if select_type=="xmind转excel":
        uploadXmind()
    elif select_type=="混沌测试-交互式":
        oneCheck()
    elif select_type=="混沌测试-直接输入(推荐)":
        onePageInput()
    elif select_type=="swagger地址转换jmeter脚本":
        jmeterScriptGen()


def jmeterScriptGen():
    url=input('输入swagger地址：example:http://192.168.xxx.xxx:port/space_name/v2/api-docs')
    # print(url)
    location=swagger2jmeter(url)
    put_file(content=open(location,mode="rb").read(),name=location.split(os.sep)[-1],label="点击下载jmeter脚本")


def uploadXmind():
    # Upload a file and save to server      
    img = open('xmindStructure.jpg', 'rb').read()  
    put_image(img)              
    f = file_upload("上传xmind文件，注意xmind节点中不要有特殊字符，空的节点使用NA标记",accept="*.xmind",placeholder='选择xmind文件')                  
    # open('asset/'+f['filename'], 'wb').write(f['content'])  
    open('temp.xmind', 'wb').write(f['content']) 
    makeCase('temp.xmind',f"{f['filename']}_testcase.xlsx")
    location=os.path.abspath('.')+os.path.sep+f"{f['filename']}_testcase.xlsx"
    put_file(content=open(location,mode="rb").read(),name=f"{f['filename']}_testcase.xlsx",label="点击下载Excel用例")





def onePageInput():
    put_markdown('''# 基础指令参考：
        ## blade create cpu load [flags]
                            --timeout string   设定运行时长，单位是秒，通用参数
                            --cpu-count string     指定 CPU 满载的个数
                            --cpu-list string      指定 CPU 满载的具体核，核索引从 0 开始 (0-3 or 1,3)
                            --cpu-percent string   指定 CPU 负载百分比，取值在 0-100
        ##                blade create disk burn
                            --path string      指定提升磁盘 io 的目录，会作用于其所在的磁盘上，默认值是 /
                            --read             触发提升磁盘读 IO 负载，会创建 600M 的文件用于读，销毁实验会自动删除
                            --size string      块大小, 单位是 M, 默认值是 10，一般不需要修改，除非想更大的提高 io 负载
                            --timeout string   设定运行时长，单位是秒，通用参数
                            --write            触发提升磁盘写 IO 负载，会根据块大小的值来写入一个文件，比如块大小是 10，则固定的块的数量是 100，则会创建 1000M 的文件，销毁实验会自动删除
        ##                blade create disk fill
                            --path string      需要填充的目录，默认值是 /
                            --size string      需要填充的文件大小，单位是 M，取值是整数，例如 --size 1024
                            --reserve string   保留磁盘大小，单位是MB。取值是不包含单位的正整数，例如 --reserve 1024。如果 size、percent、reserve 参数都存在，优先级是 percent > reserve > size
                            --percent string   指定磁盘使用率，取值是不带%号的正整数，例如 --percent 80
                            --retain-handle    是否保留填充
                            --timeout string   设定运行时长，单位是秒，通用参数
        ##                blade create mem load
                            --mem-percent string    内存使用率，取值是 0 到 100 的整数
                            --mode string   内存占用模式，有 ram 和 cache 两种，例如 --mode ram。ram 采用代码实现，可控制占用速率，优先推荐此模式；cache 是通过挂载tmpfs实现；默认值是 --mode cache
                            --reserve string    保留内存的大小，单位是MB，如果 mem-percent 参数存在，则优先使用 mem-percent 参数
                            --rate string 内存占用速率，单位是 MB/S，仅在 --mode ram 时生效
                            --timeout string   设定运行时长，单位是秒，通用参数
        ##                blade create network delay
                            --destination-ip string   目标 IP. 支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
                            --exclude-port string     排除掉的端口，默认会忽略掉通信的对端端口，目的是保留通信可用。可以指定多个，使用逗号分隔或者连接符表示范围，例如 22,8000 或者 8000-8010。 这个参数不能与 --local-port 或者 --remote-port 参数一起使用
                            --exclude-ip string       排除受影响的 IP，支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
                            --interface string        网卡设备，例如 eth0 (必要参数)
                            --local-port string       本地端口，一般是本机暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
                            --offset string           延迟时间上下浮动的值, 单位是毫秒
                            --remote-port string      远程端口，一般是要访问的外部暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
                            --time string             延迟时间，单位是毫秒 (必要参数)
                            --force                   强制覆盖已有的 tc 规则，请务必在明确之前的规则可覆盖的情况下使用
                            --ignore-peer-port        针对添加 --exclude-port 参数，报 ss 命令找不到的情况下使用，忽略排除端口
                            --timeout string          设定运行时长，单位是秒，通用参数
        ##                blade create network loss
                            --destination-ip string   目标 IP. 支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
                            --exclude-port string     排除掉的端口，默认会忽略掉通信的对端端口，目的是保留通信可用。可以指定多个，使用逗号分隔或者连接符表示范围，例如 22,8000 或者 8000-8010。 这个参数不能与 --local-port 或者 --remote-port 参数一起使用
                            --exclude-ip string       排除受影响的 IP，支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
                            --interface string        网卡设备，例如 eth0 (必要参数)
                            --local-port string       本地端口，一般是本机暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
                            --percent string          丢包百分比，取值在[0, 100]的正整数 (必要参数)
                            --remote-port string      远程端口，一般是要访问的外部暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
                            --force                   强制覆盖已有的 tc 规则，请务必在明确之前的规则可覆盖的情况下使用
                            --ignore-peer-port        针对添加 --exclude-port 参数，报 ss 命令找不到的情况下使用，忽略排除端口
                            --timeout string          设定运行时长，单位是秒，通用参数
        ##                blade create network occupy
                            --port string             指定被占用的端口，（必填项）
                            --force                   强制占用此端口，会将已使用此端口的进程杀掉
                            --timeout string          设定运行时长，单位是秒，通用参数
        ##                blade create process kill
                            --process string       进程关键词，会在整个命令行中查找
                            --process-cmd string   进程命令，只会在命令中查找
                            --count string      限制杀掉进程的数量，0 表示无限制
                            --signal string     指定杀进程的信号量，默认是 9，例如 --signal 15
                            --timeout string   设定运行时长，单位是秒，通用参数
        ##                blade create process stop
                            --process string       进程关键词，会在整个命令行中查找
                            --process-cmd string   进程命令，只会在命令中查找
                            --timeout string   设定运行时长，单位是秒，通用参数
        ##                blade create docker cpu
                            --blade-override           是否覆盖容器内已有的 chaosblade 工具，默认是 false，表示不覆盖，chaosblade 在容器内的部署路径为 /opt/chaosblade
                            --blade-tar-file string    指定本地 chaosblade-VERSION.tar.gz 工具包全路径，用于拷贝到容器内执行
                            --container-id string      目标容器 ID
                            --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
        ##                blade create docker network
                            --container-id string      目标容器 ID
                            --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
                            --image-repo string        chaosblade-tool 镜像仓库地址，默认是从 `registry.cn-hangzhou.aliyuncs.com/chaosblade`
        ##                blade create docker process
                            --blade-override           是否覆盖容器内已有的 chaosblade 工具，默认是 false，表示不覆盖，chaosblade 在容器内的部署路径为 /opt/chaosblade
                            --blade-tar-file string    指定本地 chaosblade-VERSION.tar.gz 工具包全路径，用于拷贝到容器内执行
                            --container-id string      目标容器 ID
                            --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
        ##                blade create docker container
                            --container-id string      要删除的容器 ID
                            --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
                            --force                    是否强制删除
        ''')
    json_str = textarea('请输入json串', rows=30,value=r'''{
    "data":[
      {
        "description": "主机网络限制延迟查看接口响应",
        "flag":"true", 是否运行
        "type": "docker", 类型是host 或者 docker
        "host":"192.168.xxxx.xxx",
        "port": 22,
        "user":"root",
        "passwd": "xxxxxx",
        "execCommand":"blade create network delay --time 1000 --interface eth0 --local-port 18086", 需要执行的命令
        "execCommandExpect": "success", 通常为success，可以不用改
        "dockerName": "",  如果是docker需要知道docker名称，一定要是唯一标识
        "checkCommand": "", 执行命令后的检查命令，比如CPU是否达到 70%
        "checkExpect": "", 检查的期望值
        "httpCheckCommand": { 通过http的方式进行检查，以及发送的内容，目前不支持需要登录的方式，建议手动添加token到header
          "url":"http://192.168.xxxx.xxx:18086/query",
          "method":"POST",
          "header":{},
          "cookie":{},
          "params":{"q":"show databases"},
          "json":{}
        },
        "httpExpect": "results" http检查值的返回
      },{}]}''',code={
            'mode': "json",
            'theme': 'darcula'
        })
    put_code(json_str, language='json') 
    runAndGetReport(json.loads(json_str))

    
    






def oneCheck():

    input_data={"data":[]}
    temp_data={}
    
    go_on=True
    while go_on:
        http_check_data={}
        type_data={}

        server_data = input_group("请输入主机信息",[
        input('本次测试描述', name="description"),
        input('输入服务器IP', name="host"),
        input('输入服务器端口', name="port", type=NUMBER),
        input('输入服务器用户名', name="user"),
        input('输入服务器密码', name="passwd"),
        ])

        r=RemoteRunner(server_data["host"],server_data["port"],server_data["user"],server_data["passwd"])
        upload = select("是否已经上传chaos文件:",["是","否"])
        if upload=="否":
            popup(title="注意",content="部署命令安装中。。。")
            r.exec_cmd("cd /opt;rm -rf chaos*")
            r.upload_file("chaosblade.tar.gz", "/opt")
            r.exec_cmd('cd /opt; mkdir chaos;tar -xzvf chaosblade.tar.gz -C ./chaos;')
            r.exec_cmd('echo "export PATH=$PATH:/opt/chaos/chaosblade-1.2.0/">>~/.bashrc')
            r.exec_cmd('source ~/.bashrc')
            r.upload_file("chaosblade.tar.gz", "/opt")

        select_type = select("选择被检查类型:",["主机","docker"])

        if select_type=="主机":
            type_data["type"]="host"
            type_data["dockerName"]=""
        elif select_type=="docker":
            type_data["type"]="docker"
            type_data["dockerName"]=input("请输入docker名称", name="dockerName")
        # else:
        #     popup(title="注意",content="类型选择有误！！！")
        #     select_type=None

        chaos_data = input_group("请输入chaos命令",[
            # textarea('基础指令参考', rows=20,name='comments',value='''基础指令参考：
            #     blade create cpu load [flags]
            #         --timeout string   设定运行时长，单位是秒，通用参数
            #         --cpu-count string     指定 CPU 满载的个数
            #         --cpu-list string      指定 CPU 满载的具体核，核索引从 0 开始 (0-3 or 1,3)
            #         --cpu-percent string   指定 CPU 负载百分比，取值在 0-100
            #     blade create disk burn
            #         --path string      指定提升磁盘 io 的目录，会作用于其所在的磁盘上，默认值是 /
            #         --read             触发提升磁盘读 IO 负载，会创建 600M 的文件用于读，销毁实验会自动删除
            #         --size string      块大小, 单位是 M, 默认值是 10，一般不需要修改，除非想更大的提高 io 负载
            #         --timeout string   设定运行时长，单位是秒，通用参数
            #         --write            触发提升磁盘写 IO 负载，会根据块大小的值来写入一个文件，比如块大小是 10，则固定的块的数量是 100，则会创建 1000M 的文件，销毁实验会自动删除
            #     blade create disk fill
            #         --path string      需要填充的目录，默认值是 /
            #         --size string      需要填充的文件大小，单位是 M，取值是整数，例如 --size 1024
            #         --reserve string   保留磁盘大小，单位是MB。取值是不包含单位的正整数，例如 --reserve 1024。如果 size、percent、reserve 参数都存在，优先级是 percent > reserve > size
            #         --percent string   指定磁盘使用率，取值是不带%号的正整数，例如 --percent 80
            #         --retain-handle    是否保留填充
            #         --timeout string   设定运行时长，单位是秒，通用参数
            #     blade create mem load
            #         --mem-percent string    内存使用率，取值是 0 到 100 的整数
            #         --mode string   内存占用模式，有 ram 和 cache 两种，例如 --mode ram。ram 采用代码实现，可控制占用速率，优先推荐此模式；cache 是通过挂载tmpfs实现；默认值是 --mode cache
            #         --reserve string    保留内存的大小，单位是MB，如果 mem-percent 参数存在，则优先使用 mem-percent 参数
            #         --rate string 内存占用速率，单位是 MB/S，仅在 --mode ram 时生效
            #         --timeout string   设定运行时长，单位是秒，通用参数
            #     blade create network delay
            #         --destination-ip string   目标 IP. 支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
            #         --exclude-port string     排除掉的端口，默认会忽略掉通信的对端端口，目的是保留通信可用。可以指定多个，使用逗号分隔或者连接符表示范围，例如 22,8000 或者 8000-8010。 这个参数不能与 --local-port 或者 --remote-port 参数一起使用
            #         --exclude-ip string       排除受影响的 IP，支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
            #         --interface string        网卡设备，例如 eth0 (必要参数)
            #         --local-port string       本地端口，一般是本机暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
            #         --offset string           延迟时间上下浮动的值, 单位是毫秒
            #         --remote-port string      远程端口，一般是要访问的外部暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
            #         --time string             延迟时间，单位是毫秒 (必要参数)
            #         --force                   强制覆盖已有的 tc 规则，请务必在明确之前的规则可覆盖的情况下使用
            #         --ignore-peer-port        针对添加 --exclude-port 参数，报 ss 命令找不到的情况下使用，忽略排除端口
            #         --timeout string          设定运行时长，单位是秒，通用参数
            #     blade create network loss
            #         --destination-ip string   目标 IP. 支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
            #         --exclude-port string     排除掉的端口，默认会忽略掉通信的对端端口，目的是保留通信可用。可以指定多个，使用逗号分隔或者连接符表示范围，例如 22,8000 或者 8000-8010。 这个参数不能与 --local-port 或者 --remote-port 参数一起使用
            #         --exclude-ip string       排除受影响的 IP，支持通过子网掩码来指定一个网段的IP地址, 例如 192.168.1.0/24. 则 192.168.1.0~192.168.1.255 都生效。你也可以指定固定的 IP，如 192.168.1.1 或者 192.168.1.1/32，也可以通过都号分隔多个参数，例如 192.168.1.1,192.168.2.1。
            #         --interface string        网卡设备，例如 eth0 (必要参数)
            #         --local-port string       本地端口，一般是本机暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
            #         --percent string          丢包百分比，取值在[0, 100]的正整数 (必要参数)
            #         --remote-port string      远程端口，一般是要访问的外部暴露服务的端口。可以指定多个，使用逗号分隔或者连接符表示范围，例如 80,8000-8080
            #         --force                   强制覆盖已有的 tc 规则，请务必在明确之前的规则可覆盖的情况下使用
            #         --ignore-peer-port        针对添加 --exclude-port 参数，报 ss 命令找不到的情况下使用，忽略排除端口
            #         --timeout string          设定运行时长，单位是秒，通用参数
            #     blade create network occupy
            #         --port string             指定被占用的端口，（必填项）
            #         --force                   强制占用此端口，会将已使用此端口的进程杀掉
            #         --timeout string          设定运行时长，单位是秒，通用参数
            #     blade create process kill
            #         --process string       进程关键词，会在整个命令行中查找
            #         --process-cmd string   进程命令，只会在命令中查找
            #         --count string      限制杀掉进程的数量，0 表示无限制
            #         --signal string     指定杀进程的信号量，默认是 9，例如 --signal 15
            #         --timeout string   设定运行时长，单位是秒，通用参数
            #     blade create process stop
            #         --process string       进程关键词，会在整个命令行中查找
            #         --process-cmd string   进程命令，只会在命令中查找
            #         --timeout string   设定运行时长，单位是秒，通用参数
            #     blade create docker cpu
            #         --blade-override           是否覆盖容器内已有的 chaosblade 工具，默认是 false，表示不覆盖，chaosblade 在容器内的部署路径为 /opt/chaosblade
            #         --blade-tar-file string    指定本地 chaosblade-VERSION.tar.gz 工具包全路径，用于拷贝到容器内执行
            #         --container-id string      目标容器 ID
            #         --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
            #     blade create docker network
            #         --container-id string      目标容器 ID
            #         --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
            #         --image-repo string        chaosblade-tool 镜像仓库地址，默认是从 `registry.cn-hangzhou.aliyuncs.com/chaosblade`
            #     blade create docker process
            #         --blade-override           是否覆盖容器内已有的 chaosblade 工具，默认是 false，表示不覆盖，chaosblade 在容器内的部署路径为 /opt/chaosblade
            #         --blade-tar-file string    指定本地 chaosblade-VERSION.tar.gz 工具包全路径，用于拷贝到容器内执行
            #         --container-id string      目标容器 ID
            #         --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
            #     blade create docker container
            #         --container-id string      要删除的容器 ID
            #         --docker-endpoint string   Docker server 地址，默认为本地的 /var/run/docker.sock
            #         --force                    是否强制删除
            #     '''),
        input("注入命令",name="execCommand"),
        input("注入命令期望返回", name="execCommandExpect",value="success"),
        ])
        
        select_res = select("选择检查方式:",["主机命令检查","http请求检查"])

        if select_res=="主机命令检查":
            check_data = input_group("host命令检查",[
            input("检查命令", name="checkCommand"),
            input("检查命令期望返回", name="checkExpect"),
            ])
            http_check_data["httpCheckCommand"]=""
        elif select_res=="http请求检查":
            
            check_data = input_group("http检查",[
            input("http检查接口地址", name="httpCheckCommand"),
            input("http method", name="httpCheckCommand"),
            input("http header", name="httpCheckCommand"),
            input("http param", name="httpCheckCommand"),
            input("http cookie", name="httpCheckCommand"),
            input("http json", name="httpCheckCommand"),
            input("http检查期望返回", name="httpExpect"),
            ])
            http_check_data["httpCheckCommand"]=check_data
        else:
            popup(title="注意",content="检查方式选择：有误！！！")

        # print(server_data['desc'], server_data['ip'],server_data['port'],server_data['username'],server_data['password'],
        # chaos_data['execCommand'], chaos_data['execCommandExpect'],check_data['checkCommand'],check_data['checkExpect'],check_data['httpCheckCommand'],check_data['httpExpect'])
        temp_data.update(server_data)
        temp_data.update(chaos_data)
        temp_data.update(check_data)
        temp_data.update(type_data)
        temp_data.update(http_check_data)
        temp_data["flag"]="true"
        input_data["data"].append(temp_data)



        popup(title="可直接忽略",content=json.dumps(input_data)) 

        go = select("是否继续输入下一个测试项:",["是","否"])
        if go=="是":
            go_on=True
        elif go=="否":
            go_on=False
            runAndGetReport(input_data)
            # popup(title="注意",content="正在运行测试。。。") 
            # url=generateHtmlReport(running(jf=input_data))
            # print(url)
            # close_popup()
            # # put_link(app='Result',new_window=True,name='Result')
            # # put_html(html=open(url,encoding='utf-8').readlines())
            # put_file(content=open(url,mode="rb").read(),name="result.html",label="点击下载简报")
            

def runAndGetReport(input_data):
    popup(title="注意",content="正在运行测试。。。") 
    url=generateHtmlReport(running(jf=input_data))
    print(url)
    close_popup()
    # put_link(app='Result',new_window=True,name='Result')
    # put_html(html=open(url,encoding='utf-8').readlines())
    put_file(content=open(url,mode="rb").read(),name="result.html",label="点击下载简报")


def runToolAsServer(portNum):
    start_server(app, port=portNum)

if __name__ == '__main__':
    start_server(app, port=8080)




