You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python脚本问题:为每个F5 Pool Member分配唯一属性

问题:F5池配置转换脚本的成员属性分配错误

问题背景

我需要解析旧F5系统的池配置文本,转换成新格式。目前逐行读取文件,以每行首词作为属性处理,但池内多个成员的属性值各自不同,现有脚本会把属性的最后值分配给所有成员,无法对应每个成员的专属属性。

输入配置示例

pool BASE {
    members {
        10.250.x.x:http { }
        10.250.x.x:http { }
    }
    monitor http
}
pool 10YEARS {
    members {
        10.250.x.x:http {
            session disabled
        }
        10.250.x.x:http { }
        10.250.x.x:http { }
    }
    monitor http
}
pool ADVICE {
    members {
        10.250.x.x:http {
            priority-group 70
            session disabled
        }
        10.250.x.x:http {
            priority-group 80
            session disabled
        }
        10.250.x.x:http {
            priority-group 90
        }
    }
    min-active-members 1
    monitor http
}

现有Python脚本

import json


def transform_pool(pool_name, attributes, ip_name_dict):
    transformed_line = f"ltm pool /Common/{pool_name}{{\n"
    if "monitor" in attributes:
        transformed_line += f"\tmonitor /Common/{attributes['monitor']}\n"
    if "min-active-members" in attributes:
        transformed_line += f"\tmin-active-members {attributes['min-active-members']}\n"
    if "load-balancing-mode" in attributes:
        transformed_line += (
            f"\tload-balancing-mode {attributes['load-balancing-mode']}\n"
        )
    transformed_line += "\tmembers {\n"
    for member in attributes.get("members", []):
        ip_address, port = member
        name = ip_name_dict.get(
            ip_address, ip_address
        )  # Get name from dictionary, fallback to IP if not found
        transformed_line += f"\t\t{name}:{port} {{\n"
        if "priority-group" in attributes:
            transformed_line += f"\t\tpriority-group {attributes['priority-group']}\n"
        if "state" in attributes:
            transformed_line += f"\t\tstate {attributes['state']}\n"
        if "session" in attributes:
            transformed_line += f"\t\tsession user-{attributes['session']}\n"
        transformed_line += "\t\t}\n"
    transformed_line += "\t}\n"
    transformed_line += "}\n"
    return transformed_line


def create_dict_from_file(file_name):
    with open(file_name, "r") as file:
        lines = file.readlines()

    dictionary = {}
    key = ""
    for line in lines:
        if "/Common/" in line:
            key = line.split("/Common/")[1].strip().split(" ")[0]
        if "address" in line:
            ip_address = line.split(" ")[-1].strip()
            dictionary[ip_address] = key

    return dictionary


# Use the functions
file_name = "nodes_config_full.txt"
dictionary = create_dict_from_file(file_name)


# Read original data from pool_data.txt
with open("input.txt", "r") as file:
    lines = file.readlines()

# Transform the data
transformed_lines = []
pool_name = None
attributes = {"members": []}

for line in lines:
    parts = line.strip().split()
    if len(parts) == 0:
        continue
    elif parts[0] == "pool":
        if pool_name is not None:
            transformed_lines.append(transform_pool(pool_name, attributes, dictionary))
            attributes.clear()
            attributes["members"] = []
        pool_name = parts[1]
    elif parts[0] == "monitor":
        attributes["monitor"] = parts[1]
    elif parts[0] == "min-active-members":
        attributes["min-active-members"] = parts[1]
    elif parts[0] == "load-balancing-mode":
        attributes["load-balancing-mode"] = parts[1]
    elif parts[0].count(".") == 3:  # Assume IP address format
        ip_address, port = parts[0].split(":")
        attributes["members"].append((ip_address, port))
    elif parts[0] == "priority-group":
        attributes["priority-group"] = parts[1]
    elif parts[0] == "state":
        attributes["state"] = parts[1]
    elif parts[0] == "session":
        attributes["session"] = parts[1]

# Append the last pool entry
if pool_name is not None:
    transformed_lines.append(transform_pool(pool_name, attributes, dictionary))

# Write transformed string to pools_config.txt
with open("pools_config.txt", "w") as file:
    file.writelines(transformed_lines)

print("Transformation complete. Check pools_config.txt for the output.")

当前错误输出

ltm pool /Common/BASE{
    monitor /Common/http
    members {
        WEBF03N0101:http {
        }
        WEBF03N0102:http {
        }
    }
}
ltm pool /Common/10YEARS{
    monitor /Common/http
    members {
        DMZ0103:http {
        session user-disabled
        }
        DMZ0104:http {
        session user-disabled
        }
        DMZ0107:http {
        session user-disabled
        }
    }
}
ltm pool /Common/ADVICE{
    monitor /Common/http
    min-active-members 1
    members {
        DMZ0103:http {
        priority-group 90
        session user-disabled
        }
        DMZ0104:http {
        priority-group 90
        session user-disabled
        }
        DMZ0107:http {
        priority-group 90
        session user-disabled
        }
    }
}

解决方案

问题核心在于现有脚本把成员的属性存在全局的attributes字典里,导致所有成员共享最后一次赋值的属性值。需要修改数据结构,让每个成员拥有自己的属性字典。

修改后的脚本

import json


def transform_pool(pool_name, pool_data, ip_name_dict):
    transformed_line = f"ltm pool /Common/{pool_name}{{\n"
    # 处理池级属性
    if "monitor" in pool_data:
        transformed_line += f"\tmonitor /Common/{pool_data['monitor']}\n"
    if "min-active-members" in pool_data:
        transformed_line += f"\tmin-active-members {pool_data['min-active-members']}\n"
    if "load-balancing-mode" in pool_data:
        transformed_line += f"\tload-balancing-mode {pool_data['load-balancing-mode']}\n"
    transformed_line += "\tmembers {\n"
    # 处理每个成员的专属属性
    for member in pool_data.get("members", []):
        ip_address, port, member_attrs = member
        name = ip_name_dict.get(ip_address, ip_address)
        transformed_line += f"\t\t{name}:{port} {{\n"
        if "priority-group" in member_attrs:
            transformed_line += f"\t\t\tpriority-group {member_attrs['priority-group']}\n"
        if "state" in member_attrs:
            transformed_line += f"\t\t\tstate {member_attrs['state']}\n"
        if "session" in member_attrs:
            transformed_line += f"\t\t\tsession user-{member_attrs['session']}\n"
        transformed_line += "\t\t}\n"
    transformed_line += "\t}\n"
    transformed_line += "}\n"
    return transformed_line


def create_dict_from_file(file_name):
    with open(file_name, "r") as file:
        lines = file.readlines()

    dictionary = {}
    key = ""
    for line in lines:
        if "/Common/" in line:
            key = line.split("/Common/")[1].strip().split(" ")[0]
        if "address" in line:
            ip_address = line.split(" ")[-1].strip()
            dictionary[ip_address] = key

    return dictionary


# 使用函数
file_name = "nodes_config_full.txt"
dictionary = create_dict_from_file(file_name)

# 读取原始数据
with open("input.txt", "r") as file:
    lines = file.readlines()

transformed_lines = []
pool_name = None
pool_data = {"members": []}
current_member_attrs = None  # 跟踪当前成员的属性

for line in lines:
    parts = line.strip().split()
    if len(parts) == 0:
        continue
    # 遇到新池,先处理上一个池
    elif parts[0] == "pool":
        if pool_name is not None:
            transformed_lines.append(transform_pool(pool_name, pool_data, dictionary))
            pool_data = {"members": []}
            current_member_attrs = None
        pool_name = parts[1]
    # 池级属性
    elif parts[0] == "monitor":
        pool_data["monitor"] = parts[1]
    elif parts[0] == "min-active-members":
        pool_data["min-active-members"] = parts[1]
    elif parts[0] == "load-balancing-mode":
        pool_data["load-balancing-mode"] = parts[1]
    # 遇到成员IP,初始化该成员的属性字典
    elif parts[0].count(".") == 3:
        ip_address, port = parts[0].split(":")
        current_member_attrs = {}
        pool_data["members"].append((ip_address, port, current_member_attrs))
    # 成员专属属性,赋值给当前成员的属性字典
    elif parts[0] == "priority-group":
        if current_member_attrs is not None:
            current_member_attrs["priority-group"] = parts[1]
    elif parts[0] == "state":
        if current_member_attrs is not None:
            current_member_attrs["state"] = parts[1]
    elif parts[0] == "session":
        if current_member_attrs is not None:
            current_member_attrs["session"] = parts[1]

# 处理最后一个池
if pool_name is not None:
    transformed_lines.append(transform_pool(pool_name, pool_data, dictionary))

# 写入结果
with open("pools_config.txt", "w") as file:
    file.writelines(transformed_lines)

print("转换完成,查看pools_config.txt获取输出。")

修改说明

  • 数据结构调整:把pool_data["members"]中的元素从(ip, port)改成(ip, port, member_attrs_dict),每个成员对应自己的属性字典。
  • 跟踪当前成员:新增current_member_attrs变量,在遇到成员IP时初始化,后续的成员属性都写入这个字典。
  • 转换函数适配:修改transform_pool函数,遍历成员时读取每个成员的专属属性字典,生成对应配置。

正确输出示例

ltm pool /Common/BASE{
    monitor /Common/http
    members {
        WEBF03N0101:http {
        }
        WEBF03N0102:http {
        }
    }
}
ltm pool /Common/10YEARS{
    monitor /Common/http
    members {
        DMZ0103:http {
            session user-disabled
        }
        DMZ0104:http {
        }
        DMZ0107:http {
        }
    }
}
ltm pool /Common/ADVICE{
    monitor /Common/http
    min-active-members 1
    members {
        DMZ0103:http {
            priority-group 70
            session user-disabled
        }
        DMZ0104:http {
            priority-group 80
            session user-disabled
        }
        DMZ0107:http {
            priority-group 90
        }
    }
}

内容的提问来源于stack exchange,提问作者Amar Ysabie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 19:54:55