feat: 主机变量支持 - 每台主机独立BMC/IPMI配置
- 后端: inventory生成时写入主机自定义变量(host_vars) - 前端: 主机编辑表单新增'主机变量'输入区(key=value格式) - 模板: ipmitool-bmc改为读取主机变量,未配置时用默认值 - 示例: 每台主机设置 bmc_ip=10.10.10.x,Playbook自动区分 - 变量写入inventory: web ansible_host=... bmc_ip=10.10.10.1
This commit is contained in:
@@ -459,6 +459,10 @@ func (s *AnsibleService) updateInventoryFile() {
|
|||||||
keyPath := s.resolveSSHKeyPath(h.SSHKey)
|
keyPath := s.resolveSSHKeyPath(h.SSHKey)
|
||||||
line += fmt.Sprintf(" ansible_ssh_private_key_file=%s", keyPath)
|
line += fmt.Sprintf(" ansible_ssh_private_key_file=%s", keyPath)
|
||||||
}
|
}
|
||||||
|
// 写入主机自定义变量(如 bmc_ip, bmc_user 等)
|
||||||
|
for k, v := range h.Vars {
|
||||||
|
line += fmt.Sprintf(" %s=%s", k, v)
|
||||||
|
}
|
||||||
lines = append(lines, line)
|
lines = append(lines, line)
|
||||||
}
|
}
|
||||||
lines = append(lines, "")
|
lines = append(lines, "")
|
||||||
|
|||||||
@@ -1390,22 +1390,39 @@ func GetPlaybookTemplates() []PlaybookTemplate {
|
|||||||
Description: "使用ipmitool配置服务器BMC/IPMI管理口IP地址",
|
Description: "使用ipmitool配置服务器BMC/IPMI管理口IP地址",
|
||||||
Icon: "🔧", Tags: []string{"ipmi", "bmc", "ipmitool", "hardware"},
|
Icon: "🔧", Tags: []string{"ipmi", "bmc", "ipmitool", "hardware"},
|
||||||
Content: `---
|
Content: `---
|
||||||
|
# ============================================================
|
||||||
|
# IPMI配置BMC地址
|
||||||
|
# 使用方法:
|
||||||
|
# 在主机管理中为每台主机设置变量,例如:
|
||||||
|
# bmc_ip=10.10.10.1
|
||||||
|
# bmc_netmask=255.255.255.0
|
||||||
|
# bmc_gateway=10.10.10.1
|
||||||
|
# bmc_user=admin
|
||||||
|
# bmc_password=Admin@123
|
||||||
|
# 变量会写入inventory,本Playbook自动读取每台主机的值
|
||||||
|
# 未设置的主机将使用下方默认值
|
||||||
|
# ============================================================
|
||||||
- name: IPMI配置BMC地址
|
- name: IPMI配置BMC地址
|
||||||
hosts: "{{ target_hosts | default('all') }}"
|
hosts: "{{ target_hosts | default('all') }}"
|
||||||
become: yes
|
become: yes
|
||||||
vars:
|
vars:
|
||||||
# BMC网络配置
|
# 默认值(主机变量会覆盖这些)
|
||||||
bmc_ip: "10.10.10.100"
|
default_bmc_netmask: "255.255.255.0"
|
||||||
bmc_netmask: "255.255.255.0"
|
default_bmc_gateway: "10.10.10.1"
|
||||||
bmc_gateway: "10.10.10.1"
|
default_bmc_user: "admin"
|
||||||
# BMC用户配置
|
default_bmc_password: "Admin@123"
|
||||||
bmc_user: "admin"
|
|
||||||
bmc_password: "Admin@123"
|
|
||||||
bmc_channel: 1
|
bmc_channel: 1
|
||||||
# 是否启用DHCP(设为true则忽略上面的静态IP配置)
|
# 是否启用DHCP(设为true则忽略静态IP配置)
|
||||||
bmc_dhcp: false
|
bmc_dhcp: false
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
- name: 检查BMC变量
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- bmc_ip is defined
|
||||||
|
fail_msg: "❌ 主机 {{ inventory_hostname }} 未配置 bmc_ip 变量,请在主机管理中设置"
|
||||||
|
success_msg: "✅ {{ inventory_hostname }} → BMC IP: {{ bmc_ip }}"
|
||||||
|
|
||||||
- name: 安装ipmitool
|
- name: 安装ipmitool
|
||||||
package:
|
package:
|
||||||
name: ipmitool
|
name: ipmitool
|
||||||
@@ -1439,11 +1456,11 @@ func GetPlaybookTemplates() []PlaybookTemplate {
|
|||||||
when: not bmc_dhcp
|
when: not bmc_dhcp
|
||||||
|
|
||||||
- name: 配置BMC子网掩码
|
- name: 配置BMC子网掩码
|
||||||
shell: ipmitool lan set {{ bmc_channel }} netmask {{ bmc_netmask }}
|
shell: ipmitool lan set {{ bmc_channel }} netmask {{ bmc_netmask | default(default_bmc_netmask) }}
|
||||||
when: not bmc_dhcp
|
when: not bmc_dhcp
|
||||||
|
|
||||||
- name: 配置BMC默认网关
|
- name: 配置BMC默认网关
|
||||||
shell: ipmitool lan set {{ bmc_channel }} defgw ipaddr {{ bmc_gateway }}
|
shell: ipmitool lan set {{ bmc_channel }} defgw ipaddr {{ bmc_gateway | default(default_bmc_gateway) }}
|
||||||
when: not bmc_dhcp
|
when: not bmc_dhcp
|
||||||
|
|
||||||
- name: 设置BMC为DHCP模式
|
- name: 设置BMC为DHCP模式
|
||||||
@@ -1452,8 +1469,8 @@ func GetPlaybookTemplates() []PlaybookTemplate {
|
|||||||
|
|
||||||
- name: 配置BMC用户密码
|
- name: 配置BMC用户密码
|
||||||
shell: |
|
shell: |
|
||||||
ipmitool user set name {{ bmc_channel }} 2 {{ bmc_user }} 2>/dev/null || true
|
ipmitool user set name {{ bmc_channel }} 2 {{ bmc_user | default(default_bmc_user) }} 2>/dev/null || true
|
||||||
ipmitool user set password 2 {{ bmc_password }}
|
ipmitool user set password 2 {{ bmc_password | default(default_bmc_password) }}
|
||||||
ipmitool user enable 2
|
ipmitool user enable 2
|
||||||
ipmitool channel setaccess {{ bmc_channel }} 2 privilege=4
|
ipmitool channel setaccess {{ bmc_channel }} 2 privilege=4
|
||||||
no_log: true
|
no_log: true
|
||||||
@@ -1467,12 +1484,12 @@ func GetPlaybookTemplates() []PlaybookTemplate {
|
|||||||
- name: 显示最终配置
|
- name: 显示最终配置
|
||||||
debug:
|
debug:
|
||||||
msg: |
|
msg: |
|
||||||
═══════ BMC配置完成 ═══════
|
═══════ {{ inventory_hostname }} BMC配置完成 ═══════
|
||||||
模式: {{ 'DHCP' if bmc_dhcp else '静态IP' }}
|
模式: {{ 'DHCP' if bmc_dhcp else '静态IP' }}
|
||||||
IP: {{ bmc_ip }}
|
IP: {{ bmc_ip }}
|
||||||
掩码: {{ bmc_netmask }}
|
掩码: {{ bmc_netmask | default(default_bmc_netmask) }}
|
||||||
网关: {{ bmc_gateway }}
|
网关: {{ bmc_gateway | default(default_bmc_gateway) }}
|
||||||
用户: {{ bmc_user }}
|
用户: {{ bmc_user | default(default_bmc_user) }}
|
||||||
═══════════════════════════
|
═══════════════════════════
|
||||||
{{ bmc_verify.stdout }}
|
{{ bmc_verify.stdout }}
|
||||||
`,
|
`,
|
||||||
|
|||||||
Vendored
+6
-1
@@ -845,6 +845,10 @@ async function openHostModal(id){
|
|||||||
</div>
|
</div>
|
||||||
<div class="f-group" style="margin-bottom:4px"><label class="f-label">所属主机组(可多选)</label>
|
<div class="f-group" style="margin-bottom:4px"><label class="f-label">所属主机组(可多选)</label>
|
||||||
<div class="chips" id="hf-groups">${S.groups.map(g=>`<span class="chip ${(h?.groups||[]).includes(g.name)?'on':''}" data-g="${esc(g.name)}">${esc(g.name)}</span>`).join('')||'<span class="f-hint" style="margin:0">暂无主机组,可在主机页右侧面板创建</span>'}</div>
|
<div class="chips" id="hf-groups">${S.groups.map(g=>`<span class="chip ${(h?.groups||[]).includes(g.name)?'on':''}" data-g="${esc(g.name)}">${esc(g.name)}</span>`).join('')||'<span class="f-hint" style="margin:0">暂无主机组,可在主机页右侧面板创建</span>'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="f-group" style="margin-bottom:4px"><label class="f-label">主机变量(每行一个 key=value,如 bmc_ip=10.10.10.1)</label>
|
||||||
|
<textarea class="inp mono" id="hf-vars" rows="3" placeholder="bmc_ip=10.10.10.1 bmc_user=admin bmc_password=Admin@123">${Object.entries(h?.vars||{}).map(([k,v])=>k+'='+v).join('\n')}</textarea>
|
||||||
|
<div class="f-hint">变量会写入 inventory,Playbook 中可用 {{ '{{' }} bmc_ip {{ '}}' }} 引用</div>
|
||||||
</div>`,
|
</div>`,
|
||||||
'',`<button class="btn btn-ghost" onclick="closeModal()">取消</button><button class="btn btn-primary" id="hf-save">${h?'保存修改':'添加主机'}</button>`);
|
'',`<button class="btn btn-ghost" onclick="closeModal()">取消</button><button class="btn btn-primary" id="hf-save">${h?'保存修改':'添加主机'}</button>`);
|
||||||
const seg=$('#hf-auth');
|
const seg=$('#hf-auth');
|
||||||
@@ -858,7 +862,8 @@ async function openHostModal(id){
|
|||||||
const auth=seg.querySelector('.on').dataset.v;
|
const auth=seg.querySelector('.on').dataset.v;
|
||||||
const body={name,ip,port:+$('#hf-port').value||22,username:$('#hf-user').value.trim()||'root',auth_type:auth,
|
const body={name,ip,port:+$('#hf-port').value||22,username:$('#hf-user').value.trim()||'root',auth_type:auth,
|
||||||
password:auth==='password'?$('#hf-pwd').value:'',ssh_key:auth==='sshkey'?$('#hf-key').value:'',
|
password:auth==='password'?$('#hf-pwd').value:'',ssh_key:auth==='sshkey'?$('#hf-key').value:'',
|
||||||
groups:$$('#hf-groups .chip.on').map(c=>c.dataset.g)};
|
groups:$$('#hf-groups .chip.on').map(c=>c.dataset.g),
|
||||||
|
vars:Object.fromEntries($('#hf-vars').value.split('\n').map(l=>l.trim()).filter(l=>l&&!l.startsWith('#')).map(l=>{const i=l.indexOf('=');return i>0?[l.slice(0,i).trim(),l.slice(i+1).trim()]:null;}).filter(Boolean))};
|
||||||
await withBtn($('#hf-save'),async()=>{
|
await withBtn($('#hf-save'),async()=>{
|
||||||
if(h)await api(`/api/hosts/${h.id}`,{method:'PUT',body});
|
if(h)await api(`/api/hosts/${h.id}`,{method:'PUT',body});
|
||||||
else await api('/api/hosts',{method:'POST',body});
|
else await api('/api/hosts',{method:'POST',body});
|
||||||
|
|||||||
Reference in New Issue
Block a user