fix: 讓樹狀結構符合Harmonic設定檔的結構
This commit is contained in:
parent
ad22331776
commit
d57a01f05b
|
|
@ -98704,5 +98704,329 @@
|
|||
"type": "string_or_number",
|
||||
"current_value": null,
|
||||
"updated_at": 1778235685
|
||||
},
|
||||
"ssh": {
|
||||
"hint": "Possible completions:\n[enabled] disabled enabled",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "enabled",
|
||||
"updated_at": 1778469843
|
||||
},
|
||||
"hostname": {
|
||||
"hint": "Possible completions:\n<string, min: 0 chars, max: 32 chars>[SERCOMM-COS-02]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "SERCOMM-COS-02",
|
||||
"updated_at": 1778469880
|
||||
},
|
||||
"cable mac-domain 13:0/0.0 map-advance extra-usec": {
|
||||
"hint": "Description: Map Advance Algorithm Delay\nPossible completions:\n<unsignedShort, 0 .. 20000>[100]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "100",
|
||||
"updated_at": 1778482045
|
||||
},
|
||||
"cable mac-domain 13:0/0.0 map-advance static": {
|
||||
"hint": "Possible completions:\nalgorithm Dynamic map-advance algorithm sub-menu\nextra-usec Map Advance Algorithm Delay\nmeasurements Enable/Disable latency measurement\n<cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": null,
|
||||
"updated_at": 1778482833
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv4 cable-relay-agent-ip-address": {
|
||||
"hint": "Description: giaddress of external relay agent\nPossible completions:\n<IPv4 address>[10.14.31.1]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "10.14.31.1",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv4 network-relay-agent-ip-address": {
|
||||
"hint": "Description: IPv4 address of external relay agent for dhcp-server side interface\nPossible completions:\n<IPv4 address>[10.14.23.5]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "10.14.23.5",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv4 dhcp-server-ip-address": {
|
||||
"hint": "Description: IPv4 addresses list of relay's DHCP-server\nPossible completions:\nIPv4 address",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": null,
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv4 dhcp-server-gw-ip-address": {
|
||||
"hint": "Description: IPv4 address of default route for dhcp-server side interface\nPossible completions:\n<IPv4address/netmask>[10.14.23.1/24]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "10.14.23.1/24",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv4 network-relay-agent-vlan-id": {
|
||||
"hint": "Description: VLAN id for dhcp-server side interface connection. 0 for untagged\nPossible completions:\n<unsignedShort, 0 .. 4095>[110]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "110",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv4 cable-relay-agent-nsi-ip-address": {
|
||||
"hint": "Description: IPv4 address of external relay agent for clients connections side interface\nPossible completions:\n<IPv4address/netmask>[10.14.11.4/24]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "10.14.11.4/24",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv6 cable-relay-agent-ip-address": {
|
||||
"hint": "Description: IPv6 address of external relay agent for clients connections side interface\nPossible completions:\n<IPv6 address>[2001:50:5:31::1]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "2001:50:5:31::1",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv6 network-relay-agent-ip-address": {
|
||||
"hint": "Description: IPv6 address of external relay agent for dhcp-server side interface\nPossible completions:\n<IPv6 address>[2001:50:5:23::5]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "2001:50:5:23::5",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv6 dhcp-server-ip-address": {
|
||||
"hint": "Description: IPv6 addresses list of relay's DHCP-server\nPossible completions:\nIPv6 address",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": null,
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv6 dhcp-server-gw-ip-address": {
|
||||
"hint": "Description: IPv6 address of default route for dhcp-server side interface\nPossible completions:\n<IPv6address/prefix>[2001:50:5:23::1/64]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "2001:50:5:23::1/64",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv6 network-relay-agent-vlan-id": {
|
||||
"hint": "Description: VLAN id for dhcp-server side interface connection. 0 for untagged\nPossible completions:\n<unsignedShort, 0 .. 4095>[110]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "110",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-relay ipv6 cable-relay-agent-nsi-ip-address": {
|
||||
"hint": "Description: IPv6 address of external relay agent for clients connections side interface\nPossible completions:\n<IPv6address/netmask>[2001:50:5:11::4/64]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "2001:50:5:11::4/64",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-options v4 dhcp-option-82 format": {
|
||||
"hint": "Description: DHCPv6 option 82 output format\nPossible completions:\n[bin] ascii bin",
|
||||
"options": [
|
||||
"ascii",
|
||||
"bin"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "bin",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-options v4 dhcp-option-82 hostname": {
|
||||
"hint": "Description: Set DHCPv4 option 82 hostname\nPossible completions:\n[disabled] disabled enabled",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-options v4 dhcp-option-82 upstream-description": {
|
||||
"hint": "Description: Set DHCPv4 option 82 upstream-description\nPossible completions:\n[disabled] disabled enabled",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-options v4 dhcp-option-82 us-service-class-name": {
|
||||
"hint": "Description: Set DHCPv4 82.9.5 us-service-class name sub-option\nPossible completions:\n[disabled] disabled enabled",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-options v4 dhcp-option-82 compatibility": {
|
||||
"hint": "Possible completions:",
|
||||
"options": [],
|
||||
"type": "unknown",
|
||||
"current_value": null,
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-options v6 dhcp-option-17 hostname": {
|
||||
"hint": "Description: Set DHCPv6 option 17 hostname\nPossible completions:\n[disabled] disabled enabled",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 dhcp-options v6 dhcp-option-17 upstream-description": {
|
||||
"hint": "Description: Set DHCPv6 option 17 upstream-description\nPossible completions:\n[disabled] disabled enabled",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify dhcp-server": {
|
||||
"hint": "Description: Configure a list of DHCP server ip addresses which will be used for DHCP LQ\nPossible completions:\nIP address",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": null,
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery throttling-rate": {
|
||||
"hint": "Description: Leasequery throttling rate (requests per second)\nPossible completions:\n<unsignedInt, 1 .. 32767>[500]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "500",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery downstream": {
|
||||
"hint": "Description: Enable/disable DHCP LQ on DS for IPv4\nPossible completions:\n[disabled] disabled enabled requests-per-sec",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled",
|
||||
"requests-per-sec"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery downstream requests-per-sec": {
|
||||
"hint": "Possible completions:\n<unsignedInt, 1 .. 100>[5]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "5",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery upstream": {
|
||||
"hint": "Description: Enable/disable DHCP LQ on US for IPv4\nPossible completions:\n[disabled] disabled enabled requests-per-sec",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled",
|
||||
"requests-per-sec"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery upstream requests-per-sec": {
|
||||
"hint": "Possible completions:\n<unsignedInt, 1 .. 100>[5]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "5",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery ipv6 downstream": {
|
||||
"hint": "Description: Enable/disable DHCP LQ on DS for IPv6\nPossible completions:\n[disabled] disabled enabled requests-per-sec",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled",
|
||||
"requests-per-sec"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery ipv6 downstream requests-per-sec": {
|
||||
"hint": "Possible completions:\n<unsignedInt, 1 .. 100>[5]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "5",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery ipv6 upstream": {
|
||||
"hint": "Description: Enable/disable DHCP LQ on US for IPv6\nPossible completions:\n[disabled] disabled enabled requests-per-sec",
|
||||
"options": [
|
||||
"disabled",
|
||||
"enabled",
|
||||
"requests-per-sec"
|
||||
],
|
||||
"type": "list",
|
||||
"current_value": "disabled",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 source-verify leasequery ipv6 upstream requests-per-sec": {
|
||||
"hint": "Possible completions:\n<unsignedInt, 1 .. 100>[5]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "5",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"cable bundle 1 nsi start-ipv4-address": {
|
||||
"hint": "Description: Starting address within ipv4 subnet on the CRE for the bundle nsi subnet\nPossible completions:\n<IPv4 address>[10.14.11.5]",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "10.14.11.5",
|
||||
"updated_at": 1778486389
|
||||
},
|
||||
"alias ccm": {
|
||||
"hint": "Description: Original command syntax. Valid abbreviations of the original\ncommand syntax can be entered for the command-syntax\nargument.\nPossible completions:\n<string>[clear cable modem] <cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "clear cable modem",
|
||||
"updated_at": 1778486476
|
||||
},
|
||||
"alias dcm": {
|
||||
"hint": "Description: Original command syntax. Valid abbreviations of the original\ncommand syntax can be entered for the command-syntax\nargument.\nPossible completions:\n<string>[debug cable modem] <cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "debug cable modem",
|
||||
"updated_at": 1778486476
|
||||
},
|
||||
"alias dcr": {
|
||||
"hint": "Description: Original command syntax. Valid abbreviations of the original\ncommand syntax can be entered for the command-syntax\nargument.\nPossible completions:\n<string>[debug cable rpd] <cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "debug cable rpd",
|
||||
"updated_at": 1778486476
|
||||
},
|
||||
"alias dcs": {
|
||||
"hint": "Description: Original command syntax. Valid abbreviations of the original\ncommand syntax can be entered for the command-syntax\nargument.\nPossible completions:\n<string>[debug cluster server] <cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "debug cluster server",
|
||||
"updated_at": 1778486476
|
||||
},
|
||||
"alias scr": {
|
||||
"hint": "Description: Original command syntax. Valid abbreviations of the original\ncommand syntax can be entered for the command-syntax\nargument.\nPossible completions:\n<string>[show cable rpd] <cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "show cable rpd",
|
||||
"updated_at": 1778486476
|
||||
},
|
||||
"alias scs": {
|
||||
"hint": "Description: Original command syntax. Valid abbreviations of the original\ncommand syntax can be entered for the command-syntax\nargument.\nPossible completions:\n<string>[show cluster server] <cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": "show cluster server",
|
||||
"updated_at": 1778486476
|
||||
},
|
||||
"no": {
|
||||
"hint": "Possible completions:\naaa COS AAA\nalias Create command alias.\nbanner Customize text messagees (banners) which are to be displayed before login/pass prompt\ncli Global CLI configuration\nclock\nhostname Hostname for CableOS Server Cluster\ninterface Configuration of PON and Ethernet networks\nipdr\nlogging Logging sub configuration\nnetwork\nprivilege\nradius-server\nsnmp\nsnmp-server\nspanning-tree\nssh Configure SSH access to CLI\nsystem\ntacacs-server\nusername Establish a username-based authentication system\n---\nservice Modify use of network based services\n\u001b[7m--More--\u001b[27m\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b <cr>",
|
||||
"options": [],
|
||||
"type": "string_or_number",
|
||||
"current_value": null,
|
||||
"updated_at": 1778489033
|
||||
}
|
||||
}
|
||||
182
shared.py
182
shared.py
|
|
@ -2,6 +2,8 @@
|
|||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
DEBUG_MODE = False
|
||||
|
||||
|
|
@ -22,66 +24,156 @@ CMTS_DEVICE = {
|
|||
|
||||
def parse_cli_to_tree(cli_text: str) -> dict:
|
||||
"""
|
||||
將 CMTS 的 CLI 配置文字,根據縮排轉換為巢狀 JSON (Dictionary)
|
||||
將 CMTS 的 CLI 配置文字轉換為完美的邏輯巢狀 JSON (兩階段解析法)
|
||||
"""
|
||||
# 過濾掉空行與註解 (!)
|
||||
lines = [line for line in cli_text.splitlines() if line.strip() and line.strip() != '!']
|
||||
|
||||
def parse_block(start_idx: int, base_indent: int):
|
||||
block = {}
|
||||
lines = [line.rstrip() for line in cli_text.splitlines() if line.strip()]
|
||||
|
||||
# ==========================================
|
||||
# 階段 1:建立實體樹 (依據縮排與 '!')
|
||||
# ==========================================
|
||||
def build_raw_tree(start_idx, current_indent):
|
||||
nodes = []
|
||||
i = start_idx
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
stripped = line.strip()
|
||||
|
||||
# 遇到 ! 的處理邏輯
|
||||
if stripped == '!':
|
||||
if current_indent > 0:
|
||||
return nodes, i + 1 # 子區塊遇到 !,結束並消耗該行
|
||||
else:
|
||||
i += 1 # Root 層級的 ! 只是分隔符號,直接跳過
|
||||
continue
|
||||
|
||||
indent = len(line) - len(line.lstrip())
|
||||
|
||||
if indent < base_indent:
|
||||
break
|
||||
|
||||
content = line.strip()
|
||||
# 縮排退回,隱含結束當前區塊 (不消耗該行)
|
||||
if indent < current_indent:
|
||||
return nodes, i
|
||||
|
||||
# 探測是否有子節點
|
||||
has_children = False
|
||||
if i + 1 < len(lines):
|
||||
next_indent = len(lines[i+1]) - len(lines[i+1].lstrip())
|
||||
if next_indent > base_indent:
|
||||
has_children = True
|
||||
next_line = lines[i+1]
|
||||
next_stripped = next_line.strip()
|
||||
if next_stripped != '!':
|
||||
next_indent = len(next_line) - len(next_line.lstrip())
|
||||
if next_indent > indent:
|
||||
has_children = True
|
||||
|
||||
if has_children:
|
||||
child_block, next_i = parse_block(i + 1, next_indent)
|
||||
|
||||
# 🌟 修改 1:如果區塊名稱已存在,加上帶有「空白」的虛擬索引 (例如: logging evt 1024 [1])
|
||||
if content in block:
|
||||
idx = 1
|
||||
new_key = f"{content} [{idx}]"
|
||||
while new_key in block:
|
||||
idx += 1
|
||||
new_key = f"{content} [{idx}]"
|
||||
block[new_key] = child_block
|
||||
else:
|
||||
block[content] = child_block
|
||||
|
||||
children, next_i = build_raw_tree(i + 1, next_indent)
|
||||
nodes.append({"type": "folder", "content": stripped, "children": children})
|
||||
i = next_i
|
||||
else:
|
||||
parts = content.split(maxsplit=1)
|
||||
key = parts[0]
|
||||
val = parts[1] if len(parts) == 2 else ""
|
||||
|
||||
# 🌟 修改 2:如果單行指令的 Key 已存在,加上帶有「空白」的虛擬索引 (例如: alias [1])
|
||||
if key in block:
|
||||
idx = 1
|
||||
new_key = f"{key} [{idx}]"
|
||||
while new_key in block:
|
||||
idx += 1
|
||||
new_key = f"{key} [{idx}]"
|
||||
block[new_key] = val
|
||||
else:
|
||||
block[key] = val
|
||||
|
||||
nodes.append({"type": "leaf", "content": stripped})
|
||||
i += 1
|
||||
|
||||
return block, i
|
||||
return nodes, i
|
||||
|
||||
tree, _ = parse_block(0, 0)
|
||||
return tree
|
||||
raw_tree, _ = build_raw_tree(0, 0)
|
||||
|
||||
# ==========================================
|
||||
# 階段 2:邏輯群組化與字典合併
|
||||
# ==========================================
|
||||
def deep_merge(dict1, dict2):
|
||||
"""深度合併兩個字典 (解決同名資料夾與指令群組的融合)"""
|
||||
for k, v in dict2.items():
|
||||
if k in dict1:
|
||||
if isinstance(dict1[k], dict) and isinstance(v, dict):
|
||||
deep_merge(dict1[k], v)
|
||||
else:
|
||||
# 發生碰撞時的安全機制
|
||||
idx = 1
|
||||
while f"{k} [{idx}]" in dict1:
|
||||
idx += 1
|
||||
dict1[f"{k} [{idx}]"] = v
|
||||
else:
|
||||
dict1[k] = v
|
||||
|
||||
def nest_folder_key(key_string, value_dict, target_dict):
|
||||
"""將 'cable mac-domain 13' 拆解為巢狀字典"""
|
||||
parts = key_string.split()
|
||||
current = target_dict
|
||||
for i, part in enumerate(parts):
|
||||
if i == len(parts) - 1:
|
||||
if part not in current:
|
||||
current[part] = value_dict
|
||||
else:
|
||||
if isinstance(current[part], dict) and isinstance(value_dict, dict):
|
||||
deep_merge(current[part], value_dict)
|
||||
else:
|
||||
current[f"{part} (資料夾)"] = value_dict
|
||||
else:
|
||||
if part not in current or not isinstance(current[part], dict):
|
||||
current[part] = {}
|
||||
current = current[part]
|
||||
|
||||
def group_leaves(leaf_strings):
|
||||
"""將單行指令依據共同前綴進行遞迴群組化"""
|
||||
grouped = {}
|
||||
first_word_map = defaultdict(list)
|
||||
|
||||
for s in leaf_strings:
|
||||
parts = s.split(maxsplit=1)
|
||||
first_word = parts[0]
|
||||
remainder = parts[1] if len(parts) > 1 else ""
|
||||
first_word_map[first_word].append(remainder)
|
||||
|
||||
for prefix, remainders in first_word_map.items():
|
||||
if len(remainders) == 1:
|
||||
grouped[prefix] = remainders[0]
|
||||
else:
|
||||
valid_remainders = [r for r in remainders if r]
|
||||
empty_count = len(remainders) - len(valid_remainders)
|
||||
|
||||
group_dict = {}
|
||||
if valid_remainders:
|
||||
sub_grouped = group_leaves(valid_remainders)
|
||||
|
||||
# 判斷是否全為純陣列值 (例如 IP 列表)
|
||||
is_all_empty_vals = all(not isinstance(v, dict) and v == "" for v in sub_grouped.values())
|
||||
|
||||
if is_all_empty_vals:
|
||||
for i, k in enumerate(sub_grouped.keys()):
|
||||
group_dict[f"[{i}]"] = k
|
||||
else:
|
||||
idx = 0
|
||||
for k, v in sub_grouped.items():
|
||||
if isinstance(v, dict):
|
||||
group_dict[k] = v
|
||||
elif v == "":
|
||||
group_dict[f"[{idx}]"] = k
|
||||
idx += 1
|
||||
else:
|
||||
group_dict[k] = v
|
||||
|
||||
# 補回完全沒有後續參數的指令
|
||||
for _ in range(empty_count):
|
||||
idx = 0
|
||||
while f"[{idx}]" in group_dict:
|
||||
idx += 1
|
||||
group_dict[f"[{idx}]"] = ""
|
||||
|
||||
grouped[prefix] = group_dict
|
||||
|
||||
return grouped
|
||||
|
||||
def fold_nodes(nodes):
|
||||
result = {}
|
||||
# 1. 處理資料夾 (Folders)
|
||||
for node in [n for n in nodes if n["type"] == "folder"]:
|
||||
folded_children = fold_nodes(node["children"])
|
||||
nest_folder_key(node["content"], folded_children, result)
|
||||
|
||||
# 2. 處理單行指令 (Leaves)
|
||||
leaves = [n["content"] for n in nodes if n["type"] == "leaf"]
|
||||
if leaves:
|
||||
leaf_dict = group_leaves(leaves)
|
||||
deep_merge(result, leaf_dict)
|
||||
return result
|
||||
|
||||
return fold_nodes(raw_tree)
|
||||
|
||||
def deep_split_tree(data):
|
||||
"""將平坦的 CLI 字典,依照空白字元拆分成深層巢狀結構 (具備終極型態衝突保護)"""
|
||||
|
|
|
|||
170
static/app.js
170
static/app.js
|
|
@ -89,6 +89,28 @@ function resetIdleTimer() {
|
|||
window.addEventListener('mousemove', resetIdleTimer);
|
||||
window.addEventListener('keydown', resetIdleTimer);
|
||||
window.addEventListener('click', resetIdleTimer);
|
||||
|
||||
// ==========================================
|
||||
// 🌟 輔助函數:全部展開與全部收合
|
||||
// ==========================================
|
||||
function expandAll(elementId) {
|
||||
const details = document.getElementById(`details-${elementId}`);
|
||||
if (details) {
|
||||
details.open = true; // 展開自己
|
||||
const subDetails = details.querySelectorAll('details');
|
||||
subDetails.forEach(d => d.open = true); // 展開所有子項目
|
||||
}
|
||||
}
|
||||
|
||||
function collapseAll(elementId) {
|
||||
const details = document.getElementById(`details-${elementId}`);
|
||||
if (details) {
|
||||
const subDetails = details.querySelectorAll('details');
|
||||
subDetails.forEach(d => d.open = false); // 收合所有子項目
|
||||
details.open = false; // 收合自己
|
||||
}
|
||||
}
|
||||
|
||||
// 🎨 輔助函數:解析終端機輸出,僅將錯誤/警告行標紅
|
||||
function highlightTerminalOutput(text) {
|
||||
if (!text) return '';
|
||||
|
|
@ -1514,77 +1536,114 @@ async function executeBondingConfig() {
|
|||
}
|
||||
|
||||
// ==========================================
|
||||
// 💡 6. 完整設備配置樹狀圖邏輯 (支援資料夾層級鎖定)
|
||||
// 💡 6. 完整設備配置樹狀圖邏輯 (支援資料夾層級鎖定與虛擬群組視覺優化)
|
||||
// ==========================================
|
||||
function buildTree(data, parentPath = "") {
|
||||
if (typeof data !== 'object' || data === null) {
|
||||
return `<span>${data}</span>`;
|
||||
}
|
||||
function buildTree(node, path = '') {
|
||||
let html = '';
|
||||
if (!node || typeof node !== 'object') return html;
|
||||
|
||||
let html = '<div style="margin-left: 20px;">';
|
||||
for (const key in data) {
|
||||
const value = data[key];
|
||||
const currentPath = parentPath ? `${parentPath}::${key}` : key;
|
||||
for (const [key, value] of Object.entries(node)) {
|
||||
const currentPath = path ? `${path}::${key}` : key;
|
||||
|
||||
// 🌟 產生純淨的 CLI 指令路徑 (替換 :: 為空白,並移除虛擬索引)
|
||||
const cliCommand = currentPath.replace(/::/g, ' ').replace(/\s*\[\d+\]/g, '');
|
||||
|
||||
if (typeof value === 'object' && value !== null && Object.keys(value).length > 0) {
|
||||
// 📁 渲染資料夾 (加入編輯與操作按鈕)
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
// 🌟 處理資料夾或指令群組
|
||||
const elementId = `folder-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`;
|
||||
|
||||
const hasNestedObject = Object.values(value).some(v => typeof v === 'object' && v !== null);
|
||||
const folderIcon = hasNestedObject ? '📁' : '📑';
|
||||
const folderSuffix = hasNestedObject ? '' : '<span style="color: #95a5a6; font-size: 0.85em; font-weight: normal; margin-left: 5px;">(指令群組)</span>';
|
||||
|
||||
// 🌟 定義專業的 SVG 向量圖示
|
||||
const expandAllIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 13 12 18 17 13"></polyline><polyline points="7 6 12 11 17 6"></polyline></svg>`;
|
||||
const collapseAllIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="17 11 12 6 7 11"></polyline><polyline points="17 18 12 13 7 18"></polyline></svg>`;
|
||||
|
||||
// 🌟 全部展開/收合按鈕 (使用 SVG 並加入懸停變色效果)
|
||||
const expandCollapseBtns = hasNestedObject ? `
|
||||
<span style="margin-left: 8px; display: inline-flex; gap: 6px; align-items: center;">
|
||||
<span onclick="event.stopPropagation(); event.preventDefault(); expandAll('${elementId}')"
|
||||
style="cursor: pointer; color: #95a5a6; transition: color 0.2s; display: flex; align-items: center;"
|
||||
onmouseover="this.style.color='#3498db'" onmouseout="this.style.color='#95a5a6'" title="展開全部子項目">
|
||||
${expandAllIcon}
|
||||
</span>
|
||||
<span onclick="event.stopPropagation(); event.preventDefault(); collapseAll('${elementId}')"
|
||||
style="cursor: pointer; color: #95a5a6; transition: color 0.2s; display: flex; align-items: center;"
|
||||
onmouseover="this.style.color='#e67e22'" onmouseout="this.style.color='#95a5a6'" title="收合全部子項目">
|
||||
${collapseAllIcon}
|
||||
</span>
|
||||
</span>
|
||||
` : '';
|
||||
|
||||
// ✅ 加入 title="${cliCommand}" 提供懸停提示
|
||||
html += `
|
||||
<details id="details-${elementId}" ontoggle="this.querySelector('summary').classList.toggle('is-open', this.open)">
|
||||
|
||||
<!-- 💡 確保 summary 是 flex 容器 -->
|
||||
<summary class="tree-node-header" style="font-weight: bold; color: #2c3e50; margin-top: 5px; list-style: none; display: flex; align-items: center; padding-right: 10px;">
|
||||
<details id="details-${elementId}" class="tree-folder" style="margin-top: 4px; margin-left: 20px;">
|
||||
<summary class="tree-folder-header"
|
||||
title="${cliCommand}"
|
||||
onmouseover="this.style.backgroundColor='#f1f2f6'"
|
||||
onmouseout="this.style.backgroundColor='transparent'"
|
||||
style="cursor: pointer; padding: 4px 8px; display: flex; align-items: center; outline: none; border-radius: 4px; transition: background-color 0.2s;">
|
||||
<span style="margin-right: 5px;">${folderIcon}</span>
|
||||
<b style="color: #2c3e50;">${key}</b>${folderSuffix}
|
||||
${expandCollapseBtns}
|
||||
|
||||
<span class="tree-chevron"></span>
|
||||
<span class="tree-folder-icon"></span>
|
||||
<span>${key}</span>
|
||||
|
||||
<!-- 💡 關鍵:用 margin-left: auto 將按鈕區塊推到最右側 -->
|
||||
<div style="margin-left: auto; display: flex; align-items: center;">
|
||||
<!-- ✏️ 資料夾專屬編輯按鈕 -->
|
||||
<span id="edit-btn-${elementId}" onclick="event.stopPropagation(); event.preventDefault(); startEditFolder('${currentPath}', '${elementId}')"
|
||||
style="cursor: pointer; font-size: 14px; opacity: 0.3; transition: opacity 0.2s;"
|
||||
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.3" title="鎖定並編輯此區塊">
|
||||
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.3" title="鎖定並編輯此群組">
|
||||
✏️
|
||||
</span>
|
||||
|
||||
<!-- 儲存與取消按鈕 (預設隱藏) -->
|
||||
<span id="actions-${elementId}" style="display:none; margin-left: 10px;">
|
||||
<button onclick="event.stopPropagation(); event.preventDefault(); previewFolderCLI('${currentPath}', '${elementId}')" style="background-color: #27ae60; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;">✅ 預覽指令</button>
|
||||
<button onclick="event.stopPropagation(); event.preventDefault(); cancelEditFolder('${currentPath}', '${elementId}')" style="background-color: #e74c3c; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 5px;">❌ 取消</button>
|
||||
</span>
|
||||
</div>
|
||||
</summary>
|
||||
|
||||
<div id="content-${elementId}">
|
||||
${buildTree(value, currentPath)}
|
||||
<div id="content-${elementId}" class="tree-folder-content" style="margin-left: 12px; border-left: 1px dashed #bdc3c7; padding-left: 12px;">
|
||||
${buildTree(value, currentPath)}
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
} else {
|
||||
// 📄 渲染單一設定項目 (加入獨立編輯按鈕)
|
||||
} else {
|
||||
// 🌟 處理單一設定項目 (Leaf)
|
||||
const isVirtualIndex = /^\[\d+\]$/.test(key);
|
||||
const safeValue = (value === null ? '' : value).toString().replace(/"/g, """);
|
||||
const elementId = `leaf-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`;
|
||||
|
||||
html += `
|
||||
<!-- 💡 加上 tree-leaf-node 類別,啟用 Hover 高光效果 -->
|
||||
<div class="tree-leaf-node" style="padding: 4px 10px 4px 0; color: #34495e; margin-left: 20px; border-left: 1px solid #ecf0f1; padding-left: 10px; display: flex; align-items: center;">
|
||||
<span style="margin-right: 5px;">📄</span> <b>${key}</b>:
|
||||
|
||||
let displayContent = '';
|
||||
|
||||
if (isVirtualIndex) {
|
||||
displayContent = `
|
||||
<span style="margin-right: 5px;">📄</span>
|
||||
<span class="leaf-container" id="container-${elementId}" data-path="${currentPath}" data-original="${safeValue}" style="display: inline-flex; align-items: center; min-height: 24px;">
|
||||
<span class="leaf-value" style="color: #16a085; margin-left: 5px; font-family: Courier New, monospace;">${value === null ? '' : value}</span>
|
||||
<span class="leaf-value" style="color: #16a085; font-weight: normal; font-family: Courier New, monospace;">${safeValue}</span>
|
||||
</span>
|
||||
`;
|
||||
} else {
|
||||
displayContent = `
|
||||
<span style="margin-right: 5px;">📄</span> <b>${key}</b>:
|
||||
<span class="leaf-container" id="container-${elementId}" data-path="${currentPath}" data-original="${safeValue}" style="display: inline-flex; align-items: center; min-height: 24px;">
|
||||
<span class="leaf-value" style="color: #16a085; margin-left: 5px; font-family: Courier New, monospace;">${safeValue}</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
// ✅ 加入 title="${cliCommand}" 提供懸停提示
|
||||
html += `
|
||||
<div class="tree-leaf-node"
|
||||
title="${cliCommand}"
|
||||
onmouseover="this.style.backgroundColor='#f1f2f6'"
|
||||
onmouseout="this.style.backgroundColor='transparent'"
|
||||
style="padding: 4px 8px; color: #34495e; margin-left: 20px; border-left: 1px solid #ecf0f1; display: flex; align-items: center; border-radius: 4px; transition: background-color 0.2s;">
|
||||
${displayContent}
|
||||
|
||||
<div style="margin-left: auto; display: flex; align-items: center;">
|
||||
<!-- ✏️ 單一項目專屬編輯按鈕 -->
|
||||
<span id="edit-btn-${elementId}" onclick="event.stopPropagation(); event.preventDefault(); startEditLeaf('${currentPath}', '${elementId}')"
|
||||
style="cursor: pointer; font-size: 14px; opacity: 0.3; transition: opacity 0.2s;"
|
||||
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.3" title="鎖定並編輯此項目">
|
||||
✏️
|
||||
</span>
|
||||
|
||||
<!-- 儲存與取消按鈕 (預設隱藏) -->
|
||||
<span id="actions-${elementId}" style="display:none; margin-left: 10px;">
|
||||
<button onclick="event.stopPropagation(); event.preventDefault(); previewLeafCLI('${currentPath}', '${elementId}')" style="background-color: #27ae60; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;">✅ 預覽指令</button>
|
||||
<button onclick="event.stopPropagation(); event.preventDefault(); cancelEditLeaf('${currentPath}', '${elementId}')" style="background-color: #e74c3c; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 5px;">❌ 取消</button>
|
||||
|
|
@ -1594,7 +1653,6 @@ function buildTree(data, parentPath = "") {
|
|||
`;
|
||||
}
|
||||
}
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
|
|
@ -1696,31 +1754,45 @@ function buildRealFilterTree(data, parentPath, hiddenKeys) {
|
|||
const isChecked = hiddenKeys.includes(currentPath) ? "checked" : "";
|
||||
|
||||
if (typeof value === 'object' && value !== null && Object.keys(value).length > 0) {
|
||||
// 🌟 系統設定區塊也套用相同的視覺優化
|
||||
const isVirtualGroup = Object.keys(value).every(k => /^\[\d+\]$/.test(k));
|
||||
const isVirtualIndex = /^\[\d+\]$/.test(key);
|
||||
|
||||
let displayName = key;
|
||||
let iconHtml = `<span class="tree-folder-icon"></span>`;
|
||||
|
||||
if (isVirtualGroup) {
|
||||
iconHtml = `<span style="margin-right: 5px; font-size: 16px;">📑</span>`;
|
||||
displayName = `${key} <span style="font-size: 12px; color: #7f8c8d; font-weight: normal;">(指令群組)</span>`;
|
||||
} else if (isVirtualIndex) {
|
||||
iconHtml = `<span style="margin-right: 5px; font-size: 14px;">📦</span>`;
|
||||
displayName = `<span style="color: #95a5a6; font-weight: normal;">項目 ${key}</span>`;
|
||||
}
|
||||
|
||||
html += `
|
||||
<details ontoggle="this.querySelector('summary').classList.toggle('is-open', this.open)">
|
||||
<summary class="tree-node-header" style="font-weight: bold; color: #2c3e50; margin-top: 5px; list-style: none;">
|
||||
<!-- 💡 加上 onclick 阻擋事件冒泡,避免點擊 Checkbox 時觸發資料夾展開/收合 -->
|
||||
<summary class="tree-node-header" style="font-weight: bold; color: #2c3e50; margin-top: 5px; list-style: none; display: flex; align-items: center;">
|
||||
<input type="checkbox" value="${currentPath}" class="filter-checkbox" ${isChecked}
|
||||
onclick="event.stopPropagation();" onchange="toggleChildCheckboxes(this)">
|
||||
|
||||
onclick="event.stopPropagation();" onchange="toggleChildCheckboxes(this)" style="margin-right: 8px;">
|
||||
<span class="tree-chevron"></span>
|
||||
<span class="tree-folder-icon"></span>
|
||||
<span style="flex-grow: 1;">${key}</span>
|
||||
${iconHtml}
|
||||
<span style="flex-grow: 1;">${displayName}</span>
|
||||
</summary>
|
||||
|
||||
<!-- 💡 加入特定的 class 方便後續 JS 抓取子節點 -->
|
||||
<div class="filter-children-container">
|
||||
${buildRealFilterTree(value, currentPath, hiddenKeys)}
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
} else {
|
||||
const isVirtualIndex = /^\[\d+\]$/.test(key);
|
||||
let displayName = isVirtualIndex ? `<span style="color: #95a5a6; font-weight: normal;">項目 ${key}</span>` : `<b>${key}</b>`;
|
||||
const safeValue = (value === null ? '' : value).toString().replace(/"/g, """);
|
||||
|
||||
html += `
|
||||
<div style="padding: 4px 0; color: #34495e; margin-left: 20px; border-left: 1px solid #ecf0f1; padding-left: 10px; display: flex; align-items: center;">
|
||||
<input type="checkbox" value="${currentPath}" class="filter-checkbox" ${isChecked}
|
||||
onchange="updateParentCheckboxState(this)">
|
||||
<span style="margin-right: 5px;">📄</span> <b>${key}</b>:
|
||||
onchange="updateParentCheckboxState(this)" style="margin-right: 8px;">
|
||||
<span style="margin-right: 5px;">📄</span> ${displayName}:
|
||||
<span style="color: #16a085; margin-left: 5px; font-family: Courier New, monospace;">${safeValue}</span>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,453 +1,3 @@
|
|||
{
|
||||
"hidden_keys": [
|
||||
"ssh",
|
||||
"hostname",
|
||||
"logging",
|
||||
"logging::[0]",
|
||||
"logging::evt",
|
||||
"logging::evt::1024",
|
||||
"logging::evt::1024::description",
|
||||
"logging::evt::1024::name",
|
||||
"logging::evt::1024::priority",
|
||||
"logging::evt::1024::active",
|
||||
"logging::evt::1024::destination",
|
||||
"logging::evt::1026",
|
||||
"logging::evt::1026::description",
|
||||
"logging::evt::1026::name",
|
||||
"logging::evt::1026::priority",
|
||||
"logging::evt::1026::active",
|
||||
"logging::evt::1026::destination",
|
||||
"logging::evt::1027",
|
||||
"logging::evt::1027::description",
|
||||
"logging::evt::1027::name",
|
||||
"logging::evt::1027::priority",
|
||||
"logging::evt::1027::active",
|
||||
"logging::evt::1027::destination",
|
||||
"logging::evt::1028",
|
||||
"logging::evt::1028::description",
|
||||
"logging::evt::1028::name",
|
||||
"logging::evt::1028::priority",
|
||||
"logging::evt::1028::active",
|
||||
"logging::evt::1028::destination",
|
||||
"logging::evt::1029",
|
||||
"logging::evt::1029::description",
|
||||
"logging::evt::1029::name",
|
||||
"logging::evt::1029::priority",
|
||||
"logging::evt::1029::active",
|
||||
"logging::evt::1029::destination",
|
||||
"logging::evt::1030",
|
||||
"logging::evt::1030::description",
|
||||
"logging::evt::1030::name",
|
||||
"logging::evt::1030::priority",
|
||||
"logging::evt::1030::active",
|
||||
"logging::evt::1030::destination",
|
||||
"logging::evt::1031",
|
||||
"logging::evt::1031::description",
|
||||
"logging::evt::1031::name",
|
||||
"logging::evt::1031::priority",
|
||||
"logging::evt::1031::active",
|
||||
"logging::evt::1031::destination",
|
||||
"logging::evt::1032",
|
||||
"logging::evt::1032::description",
|
||||
"logging::evt::1032::name",
|
||||
"logging::evt::1032::priority",
|
||||
"logging::evt::1032::active",
|
||||
"logging::evt::1032::destination",
|
||||
"logging::evt::1033",
|
||||
"logging::evt::1033::description",
|
||||
"logging::evt::1033::name",
|
||||
"logging::evt::1033::priority",
|
||||
"logging::evt::1033::active",
|
||||
"logging::evt::1033::destination",
|
||||
"logging::evt::1034",
|
||||
"logging::evt::1034::description",
|
||||
"logging::evt::1034::name",
|
||||
"logging::evt::1034::priority",
|
||||
"logging::evt::1034::active",
|
||||
"logging::evt::1034::destination",
|
||||
"logging::evt::1035",
|
||||
"logging::evt::1035::description",
|
||||
"logging::evt::1035::name",
|
||||
"logging::evt::1035::priority",
|
||||
"logging::evt::1035::active",
|
||||
"logging::evt::1035::destination",
|
||||
"logging::evt::1043",
|
||||
"logging::evt::1043::description",
|
||||
"logging::evt::1043::name",
|
||||
"logging::evt::1043::priority",
|
||||
"logging::evt::1043::active",
|
||||
"logging::evt::1043::destination",
|
||||
"logging::evt::1044",
|
||||
"logging::evt::1044::description",
|
||||
"logging::evt::1044::name",
|
||||
"logging::evt::1044::priority",
|
||||
"logging::evt::1044::active",
|
||||
"logging::evt::1044::destination",
|
||||
"logging::evt::1045",
|
||||
"logging::evt::1045::description",
|
||||
"logging::evt::1045::name",
|
||||
"logging::evt::1045::priority",
|
||||
"logging::evt::1045::active",
|
||||
"logging::evt::1045::destination",
|
||||
"logging::evt::1046",
|
||||
"logging::evt::1046::description",
|
||||
"logging::evt::1046::name",
|
||||
"logging::evt::1046::priority",
|
||||
"logging::evt::1046::active",
|
||||
"logging::evt::1046::destination",
|
||||
"logging::evt::1047",
|
||||
"logging::evt::1047::description",
|
||||
"logging::evt::1047::name",
|
||||
"logging::evt::1047::priority",
|
||||
"logging::evt::1047::active",
|
||||
"logging::evt::1047::destination",
|
||||
"logging::evt::1060",
|
||||
"logging::evt::1060::description",
|
||||
"logging::evt::1060::name",
|
||||
"logging::evt::1060::priority",
|
||||
"logging::evt::1060::active",
|
||||
"logging::evt::1060::destination",
|
||||
"logging::evt::1061",
|
||||
"logging::evt::1061::description",
|
||||
"logging::evt::1061::name",
|
||||
"logging::evt::1061::priority",
|
||||
"logging::evt::1061::active",
|
||||
"logging::evt::1061::destination",
|
||||
"logging::evt::1062",
|
||||
"logging::evt::1062::description",
|
||||
"logging::evt::1062::name",
|
||||
"logging::evt::1062::priority",
|
||||
"logging::evt::1062::active",
|
||||
"logging::evt::1062::destination",
|
||||
"logging::evt::1063",
|
||||
"logging::evt::1063::description",
|
||||
"logging::evt::1063::name",
|
||||
"logging::evt::1063::priority",
|
||||
"logging::evt::1063::active",
|
||||
"logging::evt::1063::destination",
|
||||
"logging::evt::1064",
|
||||
"logging::evt::1064::description",
|
||||
"logging::evt::1064::name",
|
||||
"logging::evt::1064::priority",
|
||||
"logging::evt::1064::active",
|
||||
"logging::evt::1064::destination",
|
||||
"logging::evt::1065",
|
||||
"logging::evt::1065::description",
|
||||
"logging::evt::1065::name",
|
||||
"logging::evt::1065::priority",
|
||||
"logging::evt::1065::active",
|
||||
"logging::evt::1065::destination",
|
||||
"logging::evt::1066",
|
||||
"logging::evt::1066::description",
|
||||
"logging::evt::1066::name",
|
||||
"logging::evt::1066::priority",
|
||||
"logging::evt::1066::active",
|
||||
"logging::evt::1066::destination",
|
||||
"logging::evt::1067",
|
||||
"logging::evt::1067::description",
|
||||
"logging::evt::1067::name",
|
||||
"logging::evt::1067::priority",
|
||||
"logging::evt::1067::active",
|
||||
"logging::evt::1067::destination",
|
||||
"logging::evt::1068",
|
||||
"logging::evt::1068::description",
|
||||
"logging::evt::1068::name",
|
||||
"logging::evt::1068::priority",
|
||||
"logging::evt::1068::active",
|
||||
"logging::evt::1068::destination",
|
||||
"logging::evt::1069",
|
||||
"logging::evt::1069::description",
|
||||
"logging::evt::1069::name",
|
||||
"logging::evt::1069::priority",
|
||||
"logging::evt::1069::active",
|
||||
"logging::evt::1069::destination",
|
||||
"logging::evt::1070",
|
||||
"logging::evt::1070::description",
|
||||
"logging::evt::1070::name",
|
||||
"logging::evt::1070::priority",
|
||||
"logging::evt::1070::active",
|
||||
"logging::evt::1070::destination",
|
||||
"logging::evt::1071",
|
||||
"logging::evt::1071::description",
|
||||
"logging::evt::1071::name",
|
||||
"logging::evt::1071::priority",
|
||||
"logging::evt::1071::active",
|
||||
"logging::evt::1071::destination",
|
||||
"logging::evt::1072",
|
||||
"logging::evt::1072::description",
|
||||
"logging::evt::1072::name",
|
||||
"logging::evt::1072::priority",
|
||||
"logging::evt::1072::active",
|
||||
"logging::evt::1072::destination",
|
||||
"logging::evt::1073",
|
||||
"logging::evt::1073::description",
|
||||
"logging::evt::1073::name",
|
||||
"logging::evt::1073::priority",
|
||||
"logging::evt::1073::active",
|
||||
"logging::evt::1073::destination",
|
||||
"logging::evt::1075",
|
||||
"logging::evt::1075::description",
|
||||
"logging::evt::1075::name",
|
||||
"logging::evt::1075::priority",
|
||||
"logging::evt::1075::active",
|
||||
"logging::evt::1075::destination",
|
||||
"logging::evt::1076",
|
||||
"logging::evt::1076::description",
|
||||
"logging::evt::1076::name",
|
||||
"logging::evt::1076::priority",
|
||||
"logging::evt::1076::active",
|
||||
"logging::evt::1076::destination",
|
||||
"logging::evt::1077",
|
||||
"logging::evt::1077::description",
|
||||
"logging::evt::1077::name",
|
||||
"logging::evt::1077::priority",
|
||||
"logging::evt::1077::active",
|
||||
"logging::evt::1077::destination",
|
||||
"logging::evt::2002",
|
||||
"logging::evt::2002::description",
|
||||
"logging::evt::2002::name",
|
||||
"logging::evt::2002::priority",
|
||||
"logging::evt::2002::active",
|
||||
"logging::evt::2002::destination",
|
||||
"logging::evt::2003",
|
||||
"logging::evt::2003::description",
|
||||
"logging::evt::2003::name",
|
||||
"logging::evt::2003::priority",
|
||||
"logging::evt::2003::active",
|
||||
"logging::evt::2003::destination",
|
||||
"logging::evt::2005",
|
||||
"logging::evt::2005::description",
|
||||
"logging::evt::2005::name",
|
||||
"logging::evt::2005::priority",
|
||||
"logging::evt::2005::active",
|
||||
"logging::evt::2005::destination",
|
||||
"logging::evt::2006",
|
||||
"logging::evt::2006::description",
|
||||
"logging::evt::2006::name",
|
||||
"logging::evt::2006::priority",
|
||||
"logging::evt::2006::active",
|
||||
"logging::evt::2006::destination",
|
||||
"logging::evt::2020",
|
||||
"logging::evt::2020::description",
|
||||
"logging::evt::2020::name",
|
||||
"logging::evt::2020::priority",
|
||||
"logging::evt::2020::active",
|
||||
"logging::evt::2020::destination",
|
||||
"logging::evt::2024",
|
||||
"logging::evt::2024::description",
|
||||
"logging::evt::2024::name",
|
||||
"logging::evt::2024::priority",
|
||||
"logging::evt::2024::active",
|
||||
"logging::evt::2024::destination",
|
||||
"logging::evt::2025",
|
||||
"logging::evt::2025::description",
|
||||
"logging::evt::2025::name",
|
||||
"logging::evt::2025::priority",
|
||||
"logging::evt::2025::active",
|
||||
"logging::evt::2025::destination",
|
||||
"logging::evt::2026",
|
||||
"logging::evt::2026::description",
|
||||
"logging::evt::2026::name",
|
||||
"logging::evt::2026::priority",
|
||||
"logging::evt::2026::active",
|
||||
"logging::evt::2026::destination",
|
||||
"logging::evt::2028",
|
||||
"logging::evt::2028::description",
|
||||
"logging::evt::2028::name",
|
||||
"logging::evt::2028::priority",
|
||||
"logging::evt::2028::active",
|
||||
"logging::evt::2028::destination",
|
||||
"logging::evt::2030",
|
||||
"logging::evt::2030::description",
|
||||
"logging::evt::2030::name",
|
||||
"logging::evt::2030::priority",
|
||||
"logging::evt::2030::active",
|
||||
"logging::evt::2030::destination",
|
||||
"logging::evt::2034",
|
||||
"logging::evt::2034::description",
|
||||
"logging::evt::2034::name",
|
||||
"logging::evt::2034::priority",
|
||||
"logging::evt::2034::active",
|
||||
"logging::evt::2034::destination",
|
||||
"logging::evt::2046",
|
||||
"logging::evt::2046::description",
|
||||
"logging::evt::2046::name",
|
||||
"logging::evt::2046::priority",
|
||||
"logging::evt::2046::active",
|
||||
"logging::evt::2046::destination",
|
||||
"logging::evt::2050",
|
||||
"logging::evt::2050::description",
|
||||
"logging::evt::2050::name",
|
||||
"logging::evt::2050::priority",
|
||||
"logging::evt::2050::active",
|
||||
"logging::evt::2050::destination",
|
||||
"logging::evt::2051",
|
||||
"logging::evt::2051::description",
|
||||
"logging::evt::2051::name",
|
||||
"logging::evt::2051::priority",
|
||||
"logging::evt::2051::active",
|
||||
"logging::evt::2051::destination",
|
||||
"logging::evt::2052",
|
||||
"logging::evt::2052::description",
|
||||
"logging::evt::2052::name",
|
||||
"logging::evt::2052::priority",
|
||||
"logging::evt::2052::active",
|
||||
"logging::evt::2052::destination",
|
||||
"logging::evt::2053",
|
||||
"logging::evt::2053::description",
|
||||
"logging::evt::2053::name",
|
||||
"logging::evt::2053::priority",
|
||||
"logging::evt::2053::active",
|
||||
"logging::evt::2053::destination",
|
||||
"logging::evt::2054",
|
||||
"logging::evt::2054::description",
|
||||
"logging::evt::2054::name",
|
||||
"logging::evt::2054::priority",
|
||||
"logging::evt::2054::active",
|
||||
"logging::evt::2054::destination",
|
||||
"logging::evt::2055",
|
||||
"logging::evt::2055::description",
|
||||
"logging::evt::2055::name",
|
||||
"logging::evt::2055::priority",
|
||||
"logging::evt::2055::active",
|
||||
"logging::evt::2055::destination",
|
||||
"logging::evt::2056",
|
||||
"logging::evt::2056::description",
|
||||
"logging::evt::2056::name",
|
||||
"logging::evt::2056::priority",
|
||||
"logging::evt::2056::active",
|
||||
"logging::evt::2056::destination",
|
||||
"logging::evt::2057",
|
||||
"logging::evt::2057::description",
|
||||
"logging::evt::2057::name",
|
||||
"logging::evt::2057::priority",
|
||||
"logging::evt::2057::active",
|
||||
"logging::evt::2057::destination",
|
||||
"logging::evt::2061",
|
||||
"logging::evt::2061::description",
|
||||
"logging::evt::2061::name",
|
||||
"logging::evt::2061::priority",
|
||||
"logging::evt::2061::active",
|
||||
"logging::evt::2061::destination",
|
||||
"logging::evt::2062",
|
||||
"logging::evt::2062::description",
|
||||
"logging::evt::2062::name",
|
||||
"logging::evt::2062::priority",
|
||||
"logging::evt::2062::active",
|
||||
"logging::evt::2062::destination",
|
||||
"logging::evt::2064",
|
||||
"logging::evt::2064::description",
|
||||
"logging::evt::2064::name",
|
||||
"logging::evt::2064::priority",
|
||||
"logging::evt::2064::active",
|
||||
"logging::evt::2064::destination",
|
||||
"logging::evt::2065",
|
||||
"logging::evt::2065::description",
|
||||
"logging::evt::2065::name",
|
||||
"logging::evt::2065::priority",
|
||||
"logging::evt::2065::active",
|
||||
"logging::evt::2065::destination",
|
||||
"logging::[1]",
|
||||
"logging::[2]",
|
||||
"no",
|
||||
"ipdr",
|
||||
"ipdr::[0]",
|
||||
"ipdr::[1]",
|
||||
"ipdr::[2]",
|
||||
"ipdr::[3]",
|
||||
"ipdr::[4]",
|
||||
"snmp-server",
|
||||
"snmp-server::[0]",
|
||||
"snmp-server::[1]",
|
||||
"snmp-server::[2]",
|
||||
"snmp-server::[3]",
|
||||
"snmp-server::[4]",
|
||||
"snmp-server::[5]",
|
||||
"snmp-server::[6]",
|
||||
"snmp-server::[7]",
|
||||
"snmp-server::[8]",
|
||||
"snmp-server::[9]",
|
||||
"aaa",
|
||||
"aaa::[0]",
|
||||
"aaa::[1]",
|
||||
"aaa::[2]",
|
||||
"aaa::[3]",
|
||||
"aaa::[4]",
|
||||
"aaa::[5]",
|
||||
"radius-server",
|
||||
"radius-server::[0]",
|
||||
"radius-server::[1]",
|
||||
"privilege",
|
||||
"privilege::0",
|
||||
"privilege::0::password",
|
||||
"privilege::1",
|
||||
"privilege::1::password",
|
||||
"privilege::15",
|
||||
"privilege::15::password",
|
||||
"cli",
|
||||
"cli::[0]",
|
||||
"cli::[1]",
|
||||
"packetcable",
|
||||
"packetcable::[0]",
|
||||
"packetcable::[1]",
|
||||
"packetcable::[2]",
|
||||
"packetcable::[3]",
|
||||
"packetcable::[4]",
|
||||
"packetcable::[5]",
|
||||
"system",
|
||||
"system::[0]",
|
||||
"system::[1]",
|
||||
"system::[2]",
|
||||
"system::[3]",
|
||||
"system::[4]",
|
||||
"system::[5]",
|
||||
"system::[6]",
|
||||
"system::[7]",
|
||||
"system::[8]",
|
||||
"system::[9]",
|
||||
"system::[10]",
|
||||
"system::[11]",
|
||||
"system::[12]",
|
||||
"system::[13]",
|
||||
"system::[14]",
|
||||
"system::[15]",
|
||||
"system::[16]",
|
||||
"system::[17]",
|
||||
"system::[18]",
|
||||
"system::[19]",
|
||||
"system::[20]",
|
||||
"system::[21]",
|
||||
"system::[22]",
|
||||
"system::[23]",
|
||||
"system::[24]",
|
||||
"system::[25]",
|
||||
"system::[26]",
|
||||
"system::proto-throttle",
|
||||
"system::proto-throttle::total",
|
||||
"system::proto-throttle::total::rate-pps",
|
||||
"system::proto-throttle::total::max-burst-pkts",
|
||||
"system::proto-throttle::other",
|
||||
"system::proto-throttle::other::rate-pps",
|
||||
"system::proto-throttle::other::max-burst-pkts",
|
||||
"system::proto-throttle::arp",
|
||||
"system::proto-throttle::arp::rate-pps",
|
||||
"system::proto-throttle::arp::max-burst-pkts",
|
||||
"system::proto-throttle::dhcp",
|
||||
"system::proto-throttle::dhcp::rate-pps",
|
||||
"system::proto-throttle::dhcp::max-burst-pkts",
|
||||
"system::proto-throttle::dhcpv6",
|
||||
"system::proto-throttle::dhcpv6::rate-pps",
|
||||
"system::proto-throttle::dhcpv6::max-burst-pkts",
|
||||
"system::proto-throttle::nd",
|
||||
"system::proto-throttle::nd::rate-pps",
|
||||
"system::proto-throttle::nd::max-burst-pkts",
|
||||
"system::proto-throttle::rip",
|
||||
"system::proto-throttle::rip::rate-pps",
|
||||
"system::proto-throttle::rip::max-burst-pkts",
|
||||
"system::[27]",
|
||||
"system::[28]",
|
||||
"network",
|
||||
"network::[0]",
|
||||
"network::[1]"
|
||||
]
|
||||
"hidden_keys": []
|
||||
}
|
||||
Loading…
Reference in New Issue