1+
2+ import copy
13import toml
24
35config_path = './config/config.toml'
6+ default_path = './config/full.toml'
47
58def read_db_config ():
69 with open (config_path , "r" ) as f :
@@ -14,65 +17,91 @@ def read_db_config():
1417
1518 return elastic_pwd , server_ip
1619
20+ def _deep_merge (default : dict , override : dict ) -> dict :
21+ """
22+ 递归合并:override 中的值覆盖 default。list/primitive 类型直接替换。
23+ """
24+ result = copy .deepcopy (default ) if default is not None else {}
25+ for k , v in (override or {}).items ():
26+ if k in result and isinstance (result [k ], dict ) and isinstance (v , dict ):
27+ result [k ] = _deep_merge (result [k ], v )
28+ else :
29+ result [k ] = copy .deepcopy (v )
30+ return result
31+
1732def load_agents ():
1833 from controller .src .agent import Agent
34+
35+ # 读取默认模板和用户配置
36+ default_conf = {}
37+
38+ with open (default_path , 'r' ) as f :
39+ default_conf = toml .load (f )
40+
1941 with open (config_path , 'r' ) as f :
20- config = toml .load (f )
21- elastic_config = config . get ( 'elastic' , {})
22- if 'port' not in elastic_config :
23- elastic_config [ 'port' ] = 9200
24- if 'username' not in elastic_config :
25- elastic_config [ 'username' ] = ' elastic'
26- if 'request_timeout' not in elastic_config :
27- elastic_config ['request_timeout ' ] = 10
28- if 'bulk_size' not in elastic_config :
29- elastic_config [ 'bulk_size' ] = 10
30- if 'agent_status_index' not in elastic_config :
31- elastic_config [ 'agent_status_index' ] = 'agent_status'
32- server_config = config . get ( 'server' , {})
42+ user_conf = toml .load (f )
43+
44+
45+ server_config = user_conf . get ( 'server' , {})
46+
47+ # elastic defaults (保留 elastic 部分默认项)
48+ elastic_config = user_conf . get ( 'elastic' , {})
49+ elastic_config ['address ' ] = server_config . get ( 'ip' , 'localhost' )
50+ elastic_config . setdefault ( 'port' , 9200 )
51+ elastic_config . setdefault ( 'username' , 'elastic' )
52+ elastic_config . setdefault ( 'request_timeout' , 10 )
53+ elastic_config . setdefault ( 'bulk_size' , 10 )
54+
3355 agent_dict = {}
34- if 'path' not in server_config :
35- server_config ['path' ] = 'deeptrace/ws'
36- if 'port' not in server_config :
37- server_config ['port' ] = 7901
38- for agent_config in config .get ('agents' , []):
39- if 'deeptrace_port' not in agent_config ['agent_info' ]:
40- agent_config ['agent_info' ]['deeptrace_port' ] = 52001
41- if 'workers' not in agent_config ['agent_info' ]:
42- agent_config ['agent_info' ]['workers' ] = 16
43- if 'span' not in agent_config :
44- agent_config ['span' ] = {'batch_size' : 10 }
45- if 'sender' not in agent_config :
46- agent_config ['sender' ] = {'index_name' : f"spans_{ agent_config ['agent_info' ]['agent_name' ]} " ,
47- 'mem_buffer_size' : 1 ,
48- 'file_buffer_size' : 32 ,
49- 'file_size_limit' : 1024 ,
50- 'batch_size' : 10 }
51- else :
52- if 'index_name' not in agent_config ['sender' ]:
53- agent_config ['sender' ]['index_name' ] = f"spans_{ agent_config ['agent_info' ]['agent_name' ]} "
54- if 'mem_buffer_size' not in agent_config ['sender' ]:
55- agent_config ['sender' ]['mem_buffer_size' ] = 1
56- if 'file_buffer_size' not in agent_config ['sender' ]:
57- agent_config ['sender' ]['file_buffer_size' ] = 32
58- if 'file_size_limit' not in agent_config ['sender' ]:
59- agent_config ['sender' ]['file_size_limit' ] = 1024
60- if 'batch_size' not in agent_config ['sender' ]:
61- agent_config ['sender' ]['batch_size' ] = 1024
62- if 'trace' not in agent_config :
63- agent_config ['trace' ] = {'pids' : []}
64- if 'api' not in agent_config :
65- agent_config ['api' ] = {'port' : 7899 , 'address' : '0.0.0.0' , 'workers' : 1 , 'ident' : 'deeptrace' }
66- else :
67- if 'port' not in agent_config ['api' ]:
68- agent_config ['api' ]['port' ] = 7899
69- if 'address' not in agent_config ['api' ]:
70- agent_config ['api' ]['address' ] = '0.0.0.0'
71- if 'workers' not in agent_config ['api' ]:
72- agent_config ['api' ]['workers' ] = 1
73- if 'ident' not in agent_config ['api' ]:
74- agent_config ['api' ]['ident' ] = 'deeptrace'
75- agent_dict [agent_config ['agent_info' ]['agent_name' ]] = Agent (agent_config , elastic_config , server_config )
56+
57+ # 选择默认模板中的第一个 agents 项作为模板(如果存在)
58+ default_agents = default_conf .get ('agents' , [])
59+ default_template = default_agents [0 ] if default_agents else {}
60+
61+ for user_item in user_conf .get ('agents' , []):
62+ # 用户项必须包含 agent 区块
63+ if 'agent' not in user_item :
64+ raise KeyError (f"每个 [[agents]] 项必须包含 [agents.agent] 区块,问题项:{ user_item } " )
65+
66+ # 合并:基于默认模板合并用户项(用户覆盖默认)
67+ merged_item = _deep_merge (default_template , user_item )
68+
69+ raw_agent = merged_item .get ('agent' , {})
70+ # 名称支持 agent_name 或 name
71+ agent_name = raw_agent .get ('agent_name' ) or raw_agent .get ('name' )
72+ host_password = raw_agent .get ('host_password' )
73+ host_ip = raw_agent .get ('host_ip' )
74+
75+ # 必填字段检查
76+ if not agent_name :
77+ raise KeyError (f"agents 配置缺少必填字段 agent_name/name: { raw_agent } " )
78+ if not host_password :
79+ raise KeyError (f"agents 配置缺少必填字段 host_password: { raw_agent } " )
80+ if not host_ip :
81+ raise KeyError (f"agents 配置缺少必填字段 host_ip: { raw_agent } " )
82+
83+ # 构造 agent_info(使用合并后的值)
84+ agent_info = {
85+ 'agent_name' : agent_name ,
86+ 'host_password' : host_password ,
87+ 'host_ip' : host_ip ,
88+ }
89+ if 'user_name' in raw_agent :
90+ agent_info ['user_name' ] = raw_agent ['user_name' ]
91+ if 'ssh_port' in raw_agent :
92+ try :
93+ agent_info ['ssh_port' ] = int (raw_agent ['ssh_port' ])
94+ except Exception :
95+ agent_info ['ssh_port' ] = raw_agent ['ssh_port' ]
96+
97+ # 组装 agent_config:从合并后的 item 中保留存在的区块(metric/sender/trace/ebpf/span/api)
98+ agent_config = {'agent_info' : agent_info }
99+ for key in ('metric' , 'sender' , 'trace' , 'ebpf' , 'api' , 'span' ):
100+ if key in merged_item :
101+ agent_config [key ] = merged_item [key ]
102+
103+ agent_dict [agent_name ] = Agent (agent_config , elastic_config , server_config )
104+
76105 return agent_dict
77106
78107def get_server_mode ():
0 commit comments