#!/usr/bin/env/ python
# coding: utf-8
# Copyright (c) 2024 Huawei Technologies Co., Ltd.
# openUBMC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
"""
功 能：work_build_qemu_rootfs脚本，该脚本为qemu构建测试包
"""

import os
import subprocess
import logging
import shutil

from functools import wraps

from bmcgo.tasks.task import Task
 

def log(text):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            logging.info(f"================== Start: {text} =========================")
            res = func(*args, **kw)
            logging.info(f"================== End: {text} =========================")
            logging.info(f"")
            return res
        return wrapper
    return decorator


class QemuCi(object):
    def __init__(self, src, des, lua_src, tool):
        self.src = src
        self.des = des
        self.lua_src = lua_src
        self.tool = tool

    @log("sed -i HARDWARE_ROOTKEY_ENABLE in configs")
    def __sed_config_files(self):
        cfg_files = []
        cfg_files.append("{}/opt/bmc/apps/iam/config.cfg".format(self.des))
        cfg_files.append("{}/opt/bmc/apps/event_policy/config.cfg".format(self.des))
        cfg_files.append("{}/opt/bmc/apps/firmware_mgmt/config.cfg".format(self.des))
        cfg_files.append("{}/opt/bmc/apps/key_mgmt/config.cfg".format(self.des))
        cfg_files.append("{}/opt/bmc/apps/hica/subsys/bmc_core/config.cfg".format(self.des))
        cfg_files.append("{}/opt/bmc/apps/hica/subsys/security/config.cfg".format(self.des))
        cfg_files.append("{}/opt/bmc/apps/hica/subsys/framework/config.cfg".format(self.des))
        cfg_files.append("{}/opt/bmc/apps/hica/subsys/om/config.cfg".format(self.des))
        # change value of HARDWARE_ROOTKEY_ENABLE to false, for key_mgmt start success.
        for cfg in cfg_files:
            if not self.check_path_is_existed(cfg):
                continue
            self.tool.run_command(
                "sed -i 's/HARDWARE_ROOTKEY_ENABLE = true/HARDWARE_ROOTKEY_ENABLE = false/g' {}".format(cfg), sudo=True)

    # 关闭健康状态检查功能
    @log("sed /opt/bmc/apps/hica/subsys/framework/config.cfg")
    def __sed_framework_config(self):
        des = "{}/opt/bmc/apps/hica/subsys/framework/config.cfg".format(self.des)

        if not self.check_path_is_existed(des):
            return
        self.tool.run_command('sed -i "$aIGNORE_STARTUP_CHECK_RESULT=true" {}'.format(des), sudo=True)
        self.tool.run_command('sed -i "$aIGNORE_HEALTH_CHECK_RESULT=true" {}'.format(des), sudo=True)
        self.tool.run_command('sed -i "$aCONTINUOUS_OFFLINE_TIMES=999" {}'.format(des), sudo=True)

    #重命名service.json，关闭remote_console的启动，仿真不支持该组件
    @log("mv service.json /opt/bmc/apps/remote_console/mds/service.json")
    def __mv_service_json(self):
        des = "{}/opt/bmc/apps/remote_console/mds/service.json".format(self.des)
        des_bak = "{}/opt/bmc/apps/remote_console/mds/service.json.bak".format(self.des)

        if not self.check_path_is_existed(des):
            return
        self.tool.run_command("mv {} {}".format(des, des_bak), sudo=True)

        des = "{}/opt/bmc/apps/power_mgmt/mds/service.json".format(self.des)
        des_bak = "{}/opt/bmc/apps/power_mgmt/mds/service.json.bak".format(self.des)

        if not self.check_path_is_existed(des):
            return
        self.tool.run_command("mv {} {}".format(des, des_bak), sudo=True)

    @log("cp -r /root/208-8-0-B139/virt /root/pkg_new/lib/modules/5.10.0/kernel")
    def __cp_virt(self):
        src = "{}/kernel/208-8-0-B139/virt".format(self.src)
        des = "{}/lib/modules/5.10.0/kernel".format(self.des)

        if not self.check_path_is_existed(src):
            return 

        self.tool.run_command("cp -rf {} {}".format(src, des), sudo=True)

    @log("cp -r /root/208-8-0-B139/load_kallsyms.ko /root/pkg_new/lib/modules/5.10.0/kernel/load_kallsyms.ko")
    def __cp_load_kallsyms_ko(self):
        src = "{}/kernel/208-8-0-B139/load_kallsyms.ko".format(self.src)
        des = "{}/lib/modules/5.10.0/kernel/virt".format(self.des)

        if not self.check_path_is_existed(src):
            return 

        self.tool.run_command("cp -rf {} {}".format(src, des), sudo=True)

    @log("cp /root/libsoc_adapter.so /root/pkg_new/usr/lib64")
    def __cp_libsoc_adapter_so(self):
        src = "{}/lib/libsoc_adapter.so".format(self.src)
        des = "{}/usr/lib64".format(self.des)
        if not self.check_path_is_existed(src):
            return
        self.tool.run_command("cp {} {}".format(src, des), sudo=True)

    @log("cp /root/insmod_9p_vfs.sh /root/pkg_new/opt/bmc/")
    def __cp_insmod_9p_vfs_sh(self):
        src = "{}/script/insmod_9p_vfs.sh".format(self.src)
        des = "{}/opt/bmc/".format(self.des)
        if not self.check_path_is_existed(src):
            return
        self.tool.run_command("cp {} {}".format(src, des), sudo=True)


    @log("cp /insmod_drv.sh /lib/modules")
    def __cp_insmod_drv_sh(self):
        des = "{}/lib/modules/".format(self.des)
        src = "{}/script/insmod_drv.sh".format(self.src)
        if not self.check_path_is_existed(des):
            return
        if not self.check_path_is_existed(src):
            return
        self.tool.run_command("cp {} {}".format(src, des), sudo=True)    
        
    @log("cp /data /data")
    def __cp_origin_data(self):
        src = "{}/data/".format(self.src)
        des = "{}/data/".format(self.des)

        if not self.check_path_is_existed("{}".format(src)):
            return 
        self.tool.run_command("rm -rf {}".format(des), sudo=True)
        self.tool.run_command("\cp -rf {} {}".format(src, des), sudo=True)



    @log("sed /etc/rc.d/rc.sysinit lua")
    def __sed_rc_sysinit(self):
        des = "{}/etc/rc.d/rc.sysinit".format(self.des)
        
        if not self.check_path_is_existed(des):
            return
        self.tool.run_command("sed -i '/rtos_snapshot.ko/s/^/#/g' {}".format(des), sudo=True)

        # 此处需要调用原生命令,否则只会执行>>之前
        subprocess.run(["echo", "\"chmod 755 /data/home/busybox_x\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"/data/home/busybox_x telnetd &\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"chmod 755 /data/home/busybox1711\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"chmod -R 755 /data/opt/bmc\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"chown -R apache:apache /opt/bmc/web\"", ">>", des], 
                       shell=False, capture_output=True, check=False) 
        subprocess.run(["echo", "\"chown -R secbox:secbox /data/opt/bmc/persistence.local/cooling.db\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"chown -R secbox:secbox /data/opt/bmc/persistence.local/power_strategy.db\"", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"chown -R secbox:secbox /data/opt/bmc/env_web_view.dat\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"chown -R secbox:secbox /data/opt/bmc/ps_web_view.dat\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"chown -R secbox:secbox /data/opt/bmc/powerview.txt\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"touch /dev/uart_connect\"", ">>", des], 
                       shell=False, capture_output=True, check=False)
        # 9p.sh在此行之前调用
        self.tool.run_command("sed -i '/rm -rf \/var\/lib/i sh \/opt\/bmc\/insmod_9p_vfs.sh' {}".format(des), 
                              sudo=True)
        subprocess.run(["echo", "\"#增加软连接\"", ">>", des], shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"rm /etc/shadow\"", ">>", des], shell=False, capture_output=True, check=False)
        subprocess.run(["echo", "\"ln -s /data/trust/shadow /etc/shadow\"", ">>", des], 
                       shell=False, capture_output=True, check=False)

    @log("chown -R 96:96 /opt/bmc/apps/rmcpd")
    def __chown_rmcpd(self):
        des = "{}/opt/bmc/apps/rmcpd".format(self.des)

        if not self.check_path_is_existed(des):
            return
        self.tool.run_command("chown -R 96:96 {}".format(des), sudo=True)

    # 打桩进入沙箱的组件
    @log("sed /etc/sysytemd/system")
    def __sed_rc_system(self):
        cfg_files = []
        cfg_files.append("{}/etc/systemd/system/alarm.service".format(self.des))
        cfg_files.append("{}/etc/systemd/system/energy.service".format(self.des))
        cfg_files.append("{}/etc/systemd/system/hardware.service".format(self.des))
        cfg_files.append("{}/etc/systemd/system/ras.service".format(self.des))
        cfg_files.append("{}/etc/systemd/system/interface.service".format(self.des))

        for cfg in cfg_files:
            if not self.check_path_is_existed(cfg):
                continue
            self.tool.run_command(
                "sed -i 's/AmbientCapabilities=CAP_SYS_ADMIN/AmbientCapabilities=CAP_SYS_ADMIN CAP_FOWNER "
                "CAP_SYS_MODULE CAP_DAC_OVERRIDE CAP_SYS_CHROOT CAP_NET_BIND_SERVICE CAP_MKNOD CAP_NET_ADMIN "
                "CAP_NET_RAW CAP_SYS_RAWIO CAP_CHOWN/g' {}".format(cfg), sudo=True)
        
        des = "{}/etc/systemd/system/om.service".format(self.des)
        if not self.check_path_is_existed(des):
            return
        self.tool.run_command(
            'sed -i "s/AmbientCapabilities=CAP_DAC_OVERRIDE CAP_NET_BIND_SERVICE/AmbientCapabilities='
            'CAP_SYS_ADMIN CAP_FOWNER CAP_SYS_MODULE CAP_DAC_OVERRIDE CAP_SYS_CHROOT CAP_NET_BIND_SERVICE '
            'CAP_MKNOD CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_RAWIO CAP_CHOWN/g" {}'.format(des), sudo=True)
        
        #ssdp组件更新后进入沙箱，仿真不支持，修改启动命令
        des = "{}/lib/systemd/system/bmc_ssdp.service".format(self.des)
        if not self.check_path_is_existed(des):
            return
        self.tool.run_command(
            'sed -i "s/ExecStart=\/usr\/sbin\/secbox -f \/opt\/bmc\/apps\/ssdp\/secbox.cfg '
            '\/opt\/bmc\/apps\/ssdp\/ssdp/ExecStart=\/opt\/bmc\/apps\/ssdp\/ssdp/g" {}'.format(des), sudo=True)

    @log("run qemu_action")
    def qemu_action(self):
        self.__cp_virt()
        self.__cp_load_kallsyms_ko()
        self.__cp_libsoc_adapter_so()
        self.__cp_insmod_9p_vfs_sh()
        self.__cp_insmod_drv_sh()
        self.__sed_rc_sysinit()
        self.__sed_config_files()
        self.__cp_origin_data()
        self.__chown_rmcpd()
        self.__mv_service_json()
        self.__sed_rc_system()
        self.__sed_framework_config()
        self.__sed_mc_config()

    def check_path_is_existed(self, path):
        if os.path.exists(path):
            return True
        logging.error("path not exist: {}".format(path))
        return False

    @log("del ve_libs_temp")
    def rm_ve_libs(self):
        virtio_des = "{}/virtio/".format(self.des)
        lua_code_des = "{}/lua_code/".format(self.des)
        scripts_des = "{}/scripts/".format(self.des)
        qemu_des = "{}/qemu/".format(self.des)
        qemu_luas_des = "{}/qemu_luas/".format(self.des)
        sr_des = "{}/sr/".format(self.des) 

        if self.check_path_is_existed(virtio_des):
            self.tool.run_command("\\rm -rf {}".format(virtio_des), sudo=True)
        else:
            logging.info(f"{ virtio_des } not exist")
        
        if self.check_path_is_existed(lua_code_des):
            self.tool.run_command("\\rm -rf {}".format(lua_code_des), sudo=True)
        else:
            logging.info(f"{ lua_code_des } not exist")

        if self.check_path_is_existed(scripts_des):
            self.tool.run_command("\\rm -rf {}".format(scripts_des), sudo=True)
        else:
            logging.info(f"{ scripts_des } not exist")

        if self.check_path_is_existed(qemu_des):
            self.tool.run_command("\\rm -rf {}".format(qemu_des), sudo=True)
        else:
            logging.info(f"{ qemu_des } not exist")
    
        if self.check_path_is_existed(qemu_luas_des):
            self.tool.run_command("\\rm -rf {}".format(qemu_luas_des), sudo=True)
        else:
            logging.info(f"{ qemu_luas_des } not exist")

        if self.check_path_is_existed(sr_des):
            self.tool.run_command("\\rm -rf {}".format(sr_des), sudo=True)
        else:
            logging.info(f"{ sr_des } not exist")

    @log("sed /opt/bmc/conf/mc_control.json")
    def __sed_mc_config(self):
        des = "{}/opt/bmc/conf/mc_control.json".format(self.des)
        # 关闭maca检查机制，防止因环境配置太低导致的反复maca重启
        self.tool.run_command('sed -i "s/true/false/g" {}'.format(des), sudo=True)
        timeout_mins = 2
        interval_mins = 1
        time_window_mins = 10
        max_num = 99
        self.tool.run_command('sed -i "s/: {}/: {}/g" {}'.format(timeout_mins, max_num, des), sudo=True)
        self.tool.run_command('sed -i "s/: {}/: {}/g" {}'.format(time_window_mins, max_num, des), sudo=True)
        self.tool.run_command('sed -i "s/: {}/: {}/g" {}'.format(interval_mins, max_num, des), sudo=True)
        

class TaskClass(Task):
    @staticmethod
    def copy_folder(source_path, target_path):
        # 确保目标路径存在
        os.makedirs(target_path, exist_ok=True)
        # 遍历源文件夹
        for item in os.listdir(source_path):
            source_item = os.path.join(source_path, item)
            target_item = os.path.join(target_path, item)
            # 如果是文件，直接复制
            if os.path.isfile(source_item):
                shutil.copy2(source_item, target_item)
            # 如果是文件夹，递归复制
            elif os.path.isdir(source_item):
                shutil.copytree(source_item, target_item, dirs_exist_ok=True)

    def mock_configure(self, rootfs_path):
        self.run_command(f"cp {rootfs_path}/usr/lib64/mock/libsoc_adapter_mock.so "
                         f"{rootfs_path}/usr/lib64/libsoc_adapter.so -f", sudo=True)
        self.run_command(f"rm -rf {rootfs_path}/usr/lib64/mock", sudo=True)

    def mv_web_backend(self, qemu_rootfs_path):
        """
        将源文件夹中的文件复制到目标文件夹中
        :param qemu_rootfs_path: 目标文件夹路径
        """
        # 源文件夹路径
        web_backend_path = "mock/mock/data/interface/web_backend/interface_config/mapping_config"
        source_path = os.path.join(self.config.temp_path, "qemu", web_backend_path)

        # 目标文件夹路径
        rootfs_web_backend_path = '/opt/bmc/apps/web_backend/interface_config/mapping_config'
        target_path = os.path.join(qemu_rootfs_path, rootfs_web_backend_path.lstrip('/'))

        # 确保目标路径存在
        os.makedirs(os.path.dirname(target_path), exist_ok=True)

        self.copy_folder(source_path, target_path)
        

    def clone_lua(self, work_path, trunk_path, trunk_name, src_path):
        self.chdir(work_path)
        self.run_command(f'rm -rf {os.path.join(work_path, trunk_name)}')
        self.run_command(f'git init {trunk_name}')
        self.chdir(os.path.join(work_path, trunk_name))
        self.run_command(f'git remote add origin {trunk_path}')
        self.run_command(f'git config core.sparseCheckout true')
        subprocess.run(['echo', src_path], stdout=open('.git/info/sparse-checkout', 'a'), check=False)
        self.run_command(f'git pull origin main')

    def get_luas(self):
        qemu_mock_dir = f"{self.config.temp_path}/qemu"
        os.makedirs(qemu_mock_dir, exist_ok=True)
        self.clone_lua(qemu_mock_dir, 'https://gitcode.com/openUBMC/qemu.git', 'mock', "mock")
        return qemu_mock_dir

    def qemu_rootfs_deploy(self, inner_path, qemu_name):
        # 打包原有的rootfs目录
        os.makedirs(f"{self.config.temp_path}/qemu", exist_ok=True)
        src_rootfs_path = f"{self.config.buildimg_dir}/rtos_with_driver/rootfs"
        qemu_rootfs_path = f"{self.config.temp_path}/qemu/qemu_rootfs"
        self.run_command(f"rm -rf {self.config.temp_path}/qemu/", sudo=True)
        os.makedirs(f"{self.config.temp_path}/qemu/")
        self.run_command(f"cp -a {src_rootfs_path} {qemu_rootfs_path}", sudo=True)
        self.mock_configure(qemu_rootfs_path)

        os.makedirs(inner_path, exist_ok=True)

        qemu_luas_dir = os.path.join(self.get_luas(), "mock", "mock")
        self.mv_web_backend(qemu_rootfs_path)
        QemuCi(qemu_luas_dir, qemu_rootfs_path, qemu_luas_dir, self).qemu_action()
        QemuCi(qemu_luas_dir, qemu_rootfs_path, qemu_luas_dir, self).rm_ve_libs()

        self.chdir(f"{self.config.temp_path}/qemu")

        self.run_command(f"rm -rf {qemu_name}", sudo=True)
        qemu_path = f"../{qemu_name}"
        self.chdir(f"{qemu_rootfs_path}")
        # 使用相对目录压缩
        self.pipe_command([f"sudo find .", " sudo cpio -H newc --quiet -o"], qemu_path)
        self.run_command(f"pwd", sudo=True)
        self.run_command(f"gzip -k {qemu_path}", sudo=True)
        self.run_command(f"cp -dfr {qemu_path}.gz {inner_path}", sudo=True)
        # 移动zImage
        self.run_command(
            f"cp -dfr {qemu_luas_dir}/zImage/208-8-0-B139/zImage {inner_path}/zImage_{self.config.board_name}", 
            sudo=True)


    def run(self):
        qemu_dir = os.path.join(self.config.output_path, "packet/inner")
        qemu_name = f"{self.config.board_name}_qemu.cpio"
        # tosupporte场景，在manifest.yml中配置了qemu_image字段的版本才会执行qemu构建
        if self.config.tosupporte_code == 'qemu':
            tosupporte_qemu_image = self.get_manufacture_config(f"tosupporte/{self.config.tosupporte_code}/qemu_image")
            if tosupporte_qemu_image is not None:
                qemu_dir = os.path.join(self.config.output_path, "packet", tosupporte_qemu_image.split("/")[0])
                qemu_name = tosupporte_qemu_image.split("/")[-1]
                logging.info(f"tosupport qemu image dir: {qemu_dir}, name: {qemu_name}.")
                self.qemu_rootfs_deploy(qemu_dir, qemu_name)
            else:
                logging.info(f"tosupporte: { self.config.tosupporte_code } no need build qemu package!!!")
            return