# 监视器ubuntu20.04 网络配置 bsymv
# last modify by cls on 20210830
# 查找proc剔除无线网卡
import sys
import time
import re
import os
import json
import math
import ctypes
from ctypes import *
import time
from threading import Timer
import fcntl
import subprocess
import select

#3.1 增加了服务版wifi设置 但需要重启后才能生效
#3.2 修改子网掩码的计算方法 和子网的判定
#3.3 增加多网关设定，dns新的服务解析
#3.4 增加历史网卡储存记录，避免偶然插拔导致ip变化的问题
#3.5 修改网卡跃点metric的设置
#3.7 修复海康相机查找异常的问题，修复wifidhcp的address问题
VERSION_SHOW = "version 3.7 for ubuntu2004-moju on %s" % time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))

workpath = os.path.dirname(os.path.realpath(__file__))    # 从当前文件路径中获取目录

netplan_yaml = "/etc/netplan/default_netplan.yaml"  # netplan配置文件 yaml
netplan_config_path = "/etc/netplan/netplan_prejson.txt"
netplan_config_path_wifi = "/etc/netplan/wifi_prejson.txt"  #wifi配置
# c/c++动态库
#curpath = os.getcwd()
clib_clsnt_path = workpath + "/" + "libcls_netclib.so"  # cls network tool

#clib_clsnt_path = "/root/cls_netclib/" + "libcls_netclib.so"  # cls network tool

# 解析json 配置文件内容netplan_yaml = "/etc/netplan/default_netplan.yaml"  # netplan配置文件 yaml

# netplan_yaml = "/root/bin-AOI/cnt_net/netplan_preyaml.yaml"
# netplan_config_path = "/root/bin-AOI/cnt_net/netplan_prejson.txt"  # 配置文件json（自定义）

'''--------------------------------------------tools----------------------------------------'''
# 删除转义反斜杠
def removequote(str: str):
    str = str.replace("\\\\", "\\")
    # str = str.replace("\\\"", "\"")
    return str

# 掩码ip转掩码长度
def mask2masklen(maksip: str):
    '''
    :param maksip: 输入子网掩码明文 (255.255.255.0)
    :return: 返回长度子网掩码长度 (24)
    '''
    retback = 24  # 如果解析错误 默认返回值24
    sppart = maksip.split(".")
    if len(sppart) != 4:
        return retback
    retback = 0
    for item in sppart:
        i = int(item)
        str2b = format(i, "b")  #将10进制 转成 2进制 字符串
        retback += str2b.count('1')
    return retback

#根据ip和mask判断所属子网地址 返回网段
def getIpPart(ip: str, mask: str):
    '''
    :param ip: 输入IP （192.168.103.5）
    :param mask: 子网掩码（255.255.255.0）
    :return: 网段（192.168.103.0）
    '''
    ippart = ip.split(".")
    maskpart = mask.split(".")
    if len(ippart) != 4 or len(maskpart) != 4:
        return None
    finalpart = []
    for i in range(4):
        iip = int(ippart[i])
        imask = int(maskpart[i])
        p_i = iip & imask   #算分类
        finalpart.append(str(p_i))
    return ".".join(finalpart)

#判断网卡是否是无线网卡，输入网卡名
def isWifi(name: str):
    '''
    :param name: 网卡名 （enp2s0）
    :return: True是wifi，False非wifi
    '''
    path = "/sys/class/net/%s/wireless" % name
    if os.path.exists(path):
        return True
    return False


class CCommand:
    TIMEOUT_MSG = ""

    def __init__(self, cmd):
        self.runtime = 0
        self.cmd = cmd

    # 执行函数 默认20秒超时
    def make_nonblock(self, fd):
        flags = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)

    #启动
    def command_run(self, timeout=5):
        self.proc = subprocess.Popen(
            self.cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        self.make_nonblock(self.proc.stdout)
        self.make_nonblock(self.proc.stderr)
        self.runtime = time.time()
        timer = None
        ret = {}
        out_ret = ""
        err_ret = ""
        if timeout:
            timer = Timer(timeout, self._kill_proc, [self.proc, ])
            timer.daemon = True
            #timer.setDaemon(True)  //python3.10 deprecated
            timer.start()
        while True:
            out_ret += self._read(self.proc.stdout)
            err_ret += self._read(self.proc.stderr)
            if self.proc.poll() is not None:  # 不在运行状态
                break
        out_ret += self._read(self.proc.stdout)
        err_ret += self._read(self.proc.stderr)
        self.runtime = time.time() - self.runtime  # time
        ret["OUT"] = out_ret
        ret["ERR"] = self.TIMEOUT_MSG + err_ret
        ret["STATUS"] = self.proc.returncode
        ret["RUNTIME"] = "%.3f s" % self.runtime
        if timer:
            timer.cancel()
            timer.join()
        self.proc.stdout.close()
        return ret

    def _read(self, fd):
        out = ""
        if fd is None or fd.closed:
            return out
        rd, _, _ = select.select([fd], [], [], 0)
        if rd:
            outs = fd.readlines()
            for itemout in outs:
                out += itemout.decode(encoding='utf-8')
        return out

    def _kill_proc(self, proc):
        if proc:
            try:
                proc.kill()
                self.TIMEOUT_MSG = "timeout:"
            except OSError:
                pass
            finally:
                proc.stdout.close()

#判断网卡是否存在物理连接
def netcard_connected(cardname : str):
    '''
    :param cardname: 网卡名称
    :return: 返回是否存在物理连接
    '''
    dcmd_back = CCommand("ifconfig").command_run()["OUT"]
    reback = re.findall("(\S+):\sflags=\d+<(\S+)>", dcmd_back)
    dict = {}
    for item in reback:
        name = item[0]
        brief = item[1]
        if name == "lo":
            continue
        if "RUNNING" in brief:
            dict[name] = True
        else:
            dict[name] = False
    if cardname in dict:
        return dict[cardname]
    else:
        return False

'''-------------------------------------------function-----------------------------------------'''
# 查找并确定网关信息
def chkbuildNodes_route(dictchk: dict):
    dict_back = []
    for item in dictchk.keys():
        inode = dictchk[item]
        inode.status = "normal"
        if inode.address != inode.gateway:
            part_ip = getIpPart(inode.address, inode.netmask)
            part_route = getIpPart(inode.gateway, inode.netmask)
            if part_ip == part_route:
                dict_back.append(item)
                inode.describe = "优先使用的网关"
            else:
                inode.status = "warning"
                inode.describe = "网关必须和本ip处于同一网段"
        else:
            inode.describe = "无配置网关，使用默认值"
    return dict_back    # 返回优先级较高的项


# 判定静态ip配置 是否有重复的ip
def chkbuildNodes_repetition(priority_list: list, dictchk: dict):
    #查找优先级较高的网卡设置的ip
    first_confirm = []  #已经确定占用的网段
    for item in priority_list:
        if item in dictchk.keys():
            inode = dictchk[item]
            first_confirm.append(getIpPart(inode.gateway, inode.netmask))

    # 跳过静态ip设置的项
    others_mac = list(set(priority_list) ^ dictchk.keys()) #取补集

    for inode in dictchk.values():
        if inode.mac not in others_mac:
            continue
        # 判断ip是否重复
        inode.address = inode.address if inode.address else "192.168.3.5"

        startipart2 = 2
        while True:
            if startipart2 > 253 or startipart2 < 2:
                break
            ipart = getIpPart(inode.address, inode.netmask)
            if ipart not in first_confirm :
                first_confirm.append(ipart)
                inode.describe = "设置成功" + "(%s)" % inode.describe
                break
            else:
                startipart2 = startipart2 % 255 + 1
                replace = re.sub(r'(\d+.\d+).\d+.(\d+)', r"\1.%d.\2" % startipart2, inode.address) #替换第3个段位
                inode.address = replace
                inode.status = "warning"
                inode.describe = "存在相同的网段,已经更新至 %s" % replace

        inode.netmask = "255.255.255.0"
        inode.gateway = inode.address
        inode.dns = ""
        # inode.isdhcp = False

# 判断netplan应用环境 桌面版：NetworkManager，服务版：networkd
def getNetplanContext() -> str:
    dcmd = CCommand("NetworkManager --version")
    iprouteshow = dcmd.command_run()
    back = iprouteshow["OUT"]
    workmanager = "NetworkManager"
    if back:
        workmanager = "NetworkManager"
    else:
        workmanager = "networkd"
    return workmanager

# 创建netplan规则文件
def buildNetPlanFile(listnode, listnode_wifi):
    workmanager = getNetplanContext()   #"NetworkManager" / "networkd"

    str = ""
    use_t = " " * 4  # 4空格
    str += "# This is the network config written by 'subiquity'" + "\n"
    str += "network:" + "\n"
    str += use_t + "ethernets:" + "\n"
    for node in listnode.values():
        str += node.getYamlStr()
    #wifi
    if len(listnode_wifi):
        wifi_str = ""
        for node_wifi in listnode_wifi.values():
            wifi_str += node_wifi.getYamlStr()
        if wifi_str:
            str += use_t + "wifis:\n" + wifi_str
    str += use_t + "version: 2" + "\n"
    str += use_t + "renderer: %s \n" % workmanager

    os.system("rm -f /etc/netplan/*.yaml")
    with open(netplan_yaml, "w+") as yaml_f:
        yaml_f.write(str)

# 更新状态
def finshUpdatePrejson(listnode : dict, prejson_filepath: str):
   # 等待网络配置plan 获取报表
    waitreporttime = time.time()
    afterplan_waitchk = listnode.keys()
    roundtimes = 0
    while roundtimes < 10 and afterplan_waitchk:
        savelist = []
        for item in listnode:
            itemobj = listnode[item]
            if not itemobj.use:
                continue
            if not itemobj.isdhcp and not itemobj.gateway and roundtimes > 4:
                continue
            back = itemobj.rechkafterplan(libc_clsnt)  # 检查计划执行结果
            if back:
                continue
            savelist.append(item)
        afterplan_waitchk = savelist
        roundtimes += 1
        if afterplan_waitchk:
            time.sleep(1)
    waitreporttime = time.time() - waitreporttime
    print("wait report %s:%.3f s" % (prejson_filepath,waitreporttime))

    # 保存新的配置文件
    #有线
    mapReport = {}
    report = []
    for item in listnode.values():
        report.append(item.getReport())
    mapReport["netcard_info"] = report
    jsonstr = json.dumps(mapReport, indent=4, separators=(',', ':'), ensure_ascii=False)
    jsonstr = removequote(jsonstr)
    with open(prejson_filepath, "w+") as f_report:
        f_report.write(jsonstr)

# 更新历史缓存 mac / ip
def updata_stroage_historymac():
    #mac : [{ip}, {mask}, {route}, {dns}, {isdhcp}, {iswifi}, {hotname}, {passwd}]  # {hotname}, {passwd} 仅在wifi下有用
    dict_cur_cardinfo = {}          #当前的配置信息简表
    dict_history_cardinfo = {}      #历史的储存的表
    history_mac_path = "/etc/netplan/history_mac.txt"
    # 重新获取当前表
    try:
        loaddict = {}
        #/etc/netplan/netplan_prejson.txt
        with open(netplan_config_path, "r") as json_f:
            loaddict = json.load(json_f)
            jsonnodes = loaddict["netcard_info"]

        for inode in jsonnodes:
            imac = inode["mac"]
            dict_cur_cardinfo[imac] = []
            dict_cur_cardinfo[imac].append(inode["address"])
            dict_cur_cardinfo[imac].append(inode["netmask"])
            dict_cur_cardinfo[imac].append(inode["gateway"])
            dict_cur_cardinfo[imac].append(inode["dns"])
            dict_cur_cardinfo[imac].append(inode["isdhcp"])
            dict_cur_cardinfo[imac].append(inode["ispriority"] if "ispriority" in inode else False)
            dict_cur_cardinfo[imac].append(inode["iswifi"])
            dict_cur_cardinfo[imac].append(inode["hotname"] if "hotname" in inode else "")
            dict_cur_cardinfo[imac].append(inode["passwd"] if "hotname" in inode else "")

    except json.decoder.JSONDecodeError as Argument:
        print("netplan_prejson JSONDecodeError", Argument)
    except Exception as errorvalue:
        print("netplan_prejson Exception:", repr(errorvalue))

    #/etc/netplan/history_mac.txt
    try:
        with open(history_mac_path, "r") as json_f:
            dict_history_cardinfo = json.load(json_f)
    except json.decoder.JSONDecodeError as Argument:
        print("netplan_prejson JSONDecodeError", Argument)
    except Exception as errorvalue:
        print("netplan_prejson Exception:", repr(errorvalue))

    # print("dict_history_cardinfo", dict_history_cardinfo.keys())
    # print("dict_cur_cardinfo", dict_cur_cardinfo.keys())

    # 合并两个字典 如果有一样的key 使用后一个字典里的内容
    merged_dict = {**dict_history_cardinfo, **dict_cur_cardinfo}

    # 更新文件
    os.system(f"rm -f {history_mac_path}")
    with open(history_mac_path, "w") as json_file:
        json.dump(merged_dict, json_file)

    return merged_dict


# class---------------------------------------------------------------------------
class ND_Node:
    def __init__(self):
        self.mac = ""
        self.name = ""
        self.address = ""
        self.netmask = "255.255.255.0"
        self.gateway = ""
        self.dns = ""
        self.isdhcp = False
        self.describe = ""
        self.describe_netplan = ""
        self.status = "normal"  # normal, warning, error
        self.iswifi = False
        self.use = True
        self.ispriority = False

    # 从配置文件中加载参数信息
    def uploadConfig(self):
        try:
            loaddict = {}
            # 查找当前表 /etc/netplan/netplan_prejson.txt
            with open(netplan_config_path, "r") as json_f:
                loaddict = json.load(json_f)
                jsonnodes = loaddict["netcard_info"]

            found_curmac = False
            for inode in jsonnodes:
                imac = inode["mac"]
                if imac != self.mac:
                    continue
                else:
                    found_curmac = True
                    self.updateby_current(inode)

            # 当前的mac 在使用配置表里 没有配置，查找历史配置表
            if found_curmac is False:
                self.updateby_history()

        except json.decoder.JSONDecodeError as Argument:
            print("JSONDecodeError", Argument)
            os.system("rm -f " + netplan_config_path)
        except Exception as errorvalue:
            print("Exception:", repr(errorvalue))

    def getYamlStr(self):
        str = ""
        use_t = " " * 4 #4空格

        str += (use_t * 2) + self.name + ":" + "\n"
        if self.isdhcp:
            str += (use_t * 3) + "dhcp4: " + "true" + "\n"
            if self.ispriority is True:
                str += (use_t * 3) + "dhcp4-overrides:" + "\n"
                str += (use_t * 4) + "route-metric: 100" + "\n"
            else:
                str += (use_t * 3) + "dhcp4-overrides:" + "\n"
                str += (use_t * 4) + "route-metric: 200" + "\n"
        else:
            str += (use_t * 3) + "dhcp4: " + "no" + "\n"
            str += (use_t * 3) + "routes:" + "\n"
            str += (use_t * 3) + "  - to: " + "0.0.0.0" + "/0" + "\n"
            str += (use_t * 4) + "via: " + self.gateway + "\n"
            if self.ispriority is False:
                str += (use_t * 4) + "metric: 200" + "\n"
            else:
                str += (use_t * 4) + "metric: 100" + "\n"
			
            str += (use_t * 3) + "addresses: " + "[" + self.address + "/" + "%d" % mask2masklen(self.netmask) + "]" + "\n"
		
        if not isWifi(self.name):
            str += (use_t * 3) + "mtu: " + "%d" % 9000 + "\n"
        if self.gateway and self.gateway != "0.0.0.0" and self.gateway != self.address:
            str += (use_t * 3) + "nameservers: " + "\n"
            str += (use_t * 4) + "addresses: " + "[" + self.dns + "]" + "\n"


        # if self.ispriority is True:
        #     str += (use_t * 3) + "gateway4: " + self.gateway + "\n"

        return str


    def getReport(self):
        return self.__dict__

    def rechkafterplan(self, libc):
        class CSTU_INFO(Structure):
            _fields_ = [("ip", c_char * 20),
                        ("mask", c_char * 20)]

        obs_info = CSTU_INFO()  # object struct info
        ptr_stu = byref(obs_info)  # to -> ptr
        # func
        cfun_getinfo = libc.NC_getEnInfo
        cfun_getinfo.restype = c_bool
        # libnet.argtypes = [c_char_p, CSTU_INFO]
        name = create_string_buffer(self.name.encode("utf-8"))
        rback = cfun_getinfo(name, ptr_stu)

        curip = str(obs_info.ip, 'utf-8')
        curmask = str(obs_info.mask, 'utf-8')

        # print(rback, curip, curmask)

        if rback:
            self.describe_netplan = ""

            if self.gateway or self.isdhcp:
                self.describe_netplan = ""

                # route
                iprouteshow = CCommand("ip route show").command_run()["OUT"]
                reachroute = re.findall(
                    "default\svia\s(\d+.\d+.\d+.\d+)\sdev\s(%s)\sproto.*" % self.name,
                    iprouteshow,
                    re.M
                )
                if reachroute:  #获取查询到的 设置完成的网关值（未插网线查询不到）
                    self.gateway = reachroute[0][0]
                elif self.gateway:
                    self.describe_netplan = "网关获取错误，请确认物理连接后，再次获取"
                    self.status = "warning"
                else:
                    self.gateway = self.address
                # dns
                resolvectl = CCommand("resolvectl").command_run()["OUT"]
                reachdns = re.findall(
                    "Link\s\d+\s\((%s)\)(.*\n)+\s+DNS Servers:\s(\d+.\d+.\d+.\d+)" % self.name,
                    resolvectl,
                    re.M
                )
                if reachdns:
                    self.dns = reachdns[0][2]
                elif self.dns:
                    self.describe_netplan = "DNS未配置成功"
                    self.status = "warning"

                self.address = curip
                self.netmask = curmask
                if not self.describe_netplan:
                    self.describe_netplan = "获取成功,正常使用"

                # 检查是否有和其他网卡的网段冲突
                for otheritem in gMName4Node.keys():
                    if otheritem == self.mac:
                        continue
                    objitem = gMName4Node[otheritem]
                    reachpart = getIpPart(objitem.address, objitem.netmask)
                    reachpart_self = getIpPart(self.address, self.netmask)
                    if reachpart and reachpart_self:
                        if reachpart == reachpart_self:
                            self.describe_netplan = "与%s网卡使用的网段冲突了" % objitem.name
                            self.status = "error"
            else:
                # 判断获取的和预设的是否一样
                if curip != self.address or curmask != self.netmask:
                    self.describe_netplan = "配置结果与预设值有偏差" + curip + "/" + curmask
                    self.status = "warning"
                else:
                    self.describe_netplan = "正常使用"

            return True
        else:
            #获取更新后ip失败时 先判断是否有物理连接
            command = ["ethtool", self.name]
            result = subprocess.run(command, capture_output=True, text=True)
            output = result.stdout
            if "Link detected: no" in output:
                self.describe_netplan = "网线已拔出，请确认网线物理状态。"
                self.status = "warning"
                return True

            print("link output", output)

            if self.isdhcp is True:
                self.address = ""
                self.gateway = ""
                self.dns = ""
                self.netmask = ""
            self.describe_netplan = "网卡可能未使用，请建立物理连接查看准确信息"
            self.status = "warning"

        return False

    def updateby_current(self, inode):
        if inode["address"] and inode["address"] != "0.0.0.0":
            self.address = inode["address"]
        if inode["netmask"] and inode["netmask"] != "0.0.0.0":
            self.netmask = inode["netmask"]
        if inode["gateway"] and inode["gateway"] != "0.0.0.0":
            self.gateway = inode["gateway"]
        else:   #无配置网关使用自己的address
            self.gateway = inode["address"]
        if inode["dns"] and inode["dns"] != "0.0.0.0":
            self.dns = inode["dns"]
        self.ispriority = inode["ispriority"] if "ispriority" in inode else False
        self.isdhcp = inode["isdhcp"] if "ispriority" in inode else False

    def updateby_history(self):
        history_mac_path = "/etc/netplan/history_mac.txt"
        with open(history_mac_path, "r") as json_f:
            gMHistory_Mac = json.load(json_f)

        if self.mac not in gMHistory_Mac:
            return

        value = gMHistory_Mac[self.mac]
        if len(value) != 9:
            return

        print("no found config, and found history config, used history!")
        self.address = value[0]
        self.netmask = value[1]
        self.gateway = value[2]
        self.dns     = value[3]
        self.isdhcp  = value[4]
        self.ispriority = value[5]
        self.iswifi = value[6]

class ND_Node_Wifi(ND_Node):
    def __init__(self):
        super().__init__()
        self.iswifi = True
        self.isdhcp = True
        self.use = False
        self.hotname = ""
        self.passwd = ""

    def uploadConfig(self):
        try:
            loaddict = {}
            with open(netplan_config_path_wifi, "r") as json_f:
                loaddict = json.load(json_f)
                jsonnodes = loaddict["netcard_info"]

            for inode in jsonnodes:
                imac = inode["mac"]
                if imac not in self.mac:
                    continue
                self.updateby_current(inode)
                # wifi
                if inode["use"]:
                    self.use = inode["use"]
                if inode["hotname"]:
                    self.hotname = inode["hotname"]
                if inode["passwd"]:
                    self.passwd = inode["passwd"]

        except json.decoder.JSONDecodeError as Argument:
            print("JSONDecodeError", Argument)
            os.system("rm -f " + netplan_config_path_wifi)
        except Exception as errorvalue:
            print("Exception:", repr(errorvalue))

    def getYamlStr(self):
        if not self.use:
            return ""
        if not self.hotname or not self.passwd:
            return ""
        use_t = " " * 4
        str = super().getYamlStr()
        str += (use_t * 3) + "access-points: \n"
        str += (use_t * 4) + "\"%s\": \n" % self.hotname
        str += (use_t * 5) + "password: \"%s\"\n" % self.passwd
        return str

# main---------------------------------------------------------------------------
if __name__ == "__main__":
    print(VERSION_SHOW)

    cdll.LoadLibrary(clib_clsnt_path)
    libc_clsnt = CDLL(clib_clsnt_path)

    os.system("mkdir -p ./cnt_net")
    gMName4Node = {} #普通网卡 # key:网卡名 value:对象
    gMName4Node_wifi = {}   #wifi信息
    gMHistory_Mac = updata_stroage_historymac()     #历史mac表

    # /proc/net/dev : listproc_cardname ['enp4s0', 'enp2s0', 'lo', 'enp3s0', 'enp6s0', 'enp5s0']
    # 获取mac和网卡名对应的字典 : dict_mac2name {'b0:41:6f:04:5a:5e': 'eno1', 'b0:41:6f:04:4b:d6': 'enp2s0'}
    dict_mac2name = {} # 有线网卡
    dict_mac2name_wifi = {} #wifi列表
    with open("/proc/net/dev", "r") as f:
        str_cmd = f.read()
    listproc_cardname = re.findall("\s+(\S+):", str_cmd)    #所有发现的网卡
    for iproc in listproc_cardname:
        if iproc == "lo":
            continue
        macFile = "/sys/class/net/%s/address" % iproc  # iproc网卡名称
        with open(macFile, "r") as fitem:
            mac = fitem.read()
            mac = mac.strip("\n")  # strip()可以去除头尾指定字符
            #在服务版 networkd 管理器才使用wifi配置, #“NetworkManager”，"networkd"
            if isWifi(iproc) and getNetplanContext() == "networkd":
                dict_mac2name_wifi[mac] = iproc
            elif not isWifi(iproc):
                dict_mac2name[mac] = iproc
    # if len(dict_mac2name_wifi) == 0:
    #     #清除之前WiFi配置 “wifi_prejson.txt”
    #     os.system("rm -f %s" % netplan_config_path_wifi)
    print(dict_mac2name)

    '''初始化列表node'''
    for imac in dict_mac2name:
        inode = ND_Node()
        inode.mac = imac
        inode.name = dict_mac2name[imac]
        inode.uploadConfig()
        gMName4Node[imac] = inode

    '''初始化wifi列表'''
    for imac_wifi in dict_mac2name_wifi:
        inode = ND_Node_Wifi()
        inode.mac = imac_wifi
        inode.name = name = dict_mac2name_wifi[imac_wifi]
        inode.uploadConfig()
        gMName4Node_wifi[imac_wifi] = inode

    '''重新构建配置文件, 处理网段重复问题(有线)'''
    priority_list = chkbuildNodes_route(gMName4Node)    #返回优先级较高的网卡

    # 剩余项全部按静态处理
    chkbuildNodes_repetition(priority_list, gMName4Node)

    '''解析成netplan文件'''
    buildNetPlanFile(gMName4Node, gMName4Node_wifi)

    # dict_newhistory = updata_stroage_historymac()
    # print(dict_newhistory)
    # exit(0)

    # 执行网络计划
    dcmd = CCommand("netplan apply")
    netplan_back = dcmd.command_run()
    print("netplan apply time:" + netplan_back["RUNTIME"])
    # rule = "(\d+(\.\d+)?)"
    # timeout = re.findall(rule,netplan_back["RUNTIME"])
    # if float(timeout[0][0]) < 5:
    time.sleep(2)  # 延时两秒读网络配置
    # 回滚 更新执行结果
    finshUpdatePrejson(gMName4Node, netplan_config_path)
    finshUpdatePrejson(gMName4Node_wifi, netplan_config_path_wifi)

    ''' 指定网卡为千兆网'''
    # for inode in gMName4Node.values():
    #     cmd = "ethtool -s %s speed 1000" % inode.name # duplex full autoneg off
    #     dcmd = CCommand(cmd)
    #     dcmd.command_run()
    #     print("finish -> " + cmd)

    ''' 更新历史缓存 '''
    updata_stroage_historymac()

    print("finish")
