Files
aiops/test_monitoring_es.py
T
attacker a04d815efe fix(log): ES 7.2.0 time format .SSSZ + index dropdown + demo always visible
- ES query 502 fix: remove +00:00 conversion, use native .SSSZ format
  (ES 7.2.0 date_time = strict_date_optional_time only, rejects +00:00/epoch)
- parseLogTime: use time.RFC3339Nano for fractional seconds
- new API GET /log-datasources/indices to list ES indices via proxy
- LogDatasourceDialog: add '获取索引' button + Select dropdown for ES
- LogDatasourcePage: always show demo sources alongside real ones
2026-06-27 09:45:21 +00:00

185 lines
6.8 KiB
Python

#!/usr/bin/env python3
"""
测试 .monitoring-es-7-* 数据源全链路
用法: python3 test_monitoring_es.py
"""
import urllib.request
import urllib.error
import json
import time
AIOPS_URL = "http://10.10.10.75:8080"
ES_URL = "http://10.10.10.156:9200"
INDEX = ".monitoring-es-7-*"
def req(method, path, body=None, token=None):
url = f"{AIOPS_URL}{path}"
data = json.dumps(body).encode() if body else None
r = urllib.request.Request(url, data=data, method=method)
r.add_header('Content-Type', 'application/json')
if token:
r.add_header('Authorization', f'Bearer {token}')
resp = urllib.request.urlopen(r, timeout=15)
return json.loads(resp.read())
def login():
result = req('POST', '/api/v1/auth/login', {"username": "admin", "password": "123456"})
return result['data']['token']
def test_direct_es():
"""1. 直连 ES 测试"""
print("=" * 60)
print("1. 直连 ES — 集群信息")
try:
body = json.dumps({"query": {"match_all": {}}, "size": 0}).encode()
r = urllib.request.Request(f"{ES_URL}/{INDEX}/_search", data=body, method='POST')
r.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(r, timeout=10)
d = json.loads(resp.read())
total = d['hits']['total']
print(f" ✓ 索引 {INDEX} 命中: {total} 条")
except Exception as e:
print(f" ✗ 直连失败: {e}")
return False
print("\n 采样一条原始文档:")
try:
body = json.dumps({"query": {"match_all": {}}, "size": 1}).encode()
r = urllib.request.Request(f"{ES_URL}/{INDEX}/_search", data=body, method='POST')
r.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(r, timeout=10)
d = json.loads(resp.read())
src = d['hits']['hits'][0]['_source']
has_ts_at = '@timestamp' in src
has_ts = 'timestamp' in src
print(f" @timestamp: {has_ts_at}, timestamp: {has_ts}")
print(f" timestamp 值: {src.get('timestamp', 'N/A')[:35]}")
print(f" 可用字段: {list(src.keys())[:10]}")
except Exception as e:
print(f" ✗ 采样失败: {e}")
print("\n 时间范围查询测试:")
tests = [
("Z 格式 (原问题)", "@timestamp", "2026-06-21T00:00:00.000Z", "2026-06-28T00:00:00.000Z"),
("+00:00 格式 (修复后)", "@timestamp", "2026-06-21T00:00:00.000+00:00", "2026-06-28T00:00:00.000+00:00"),
("+00:00 + timestamp字段", "timestamp", "2026-06-21T00:00:00.000+00:00", "2026-06-28T00:00:00.000+00:00"),
]
for name, field, gte, lte in tests:
try:
q = {
"size": 0,
"query": {"range": {field: {"gte": gte, "lte": lte}}}
}
body = json.dumps(q).encode()
r = urllib.request.Request(f"{ES_URL}/{INDEX}/_search", data=body, method='POST')
r.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(r, timeout=10)
d = json.loads(resp.read())
if 'error' in d:
print(f" ✗ {name}: ES错误")
else:
total = d['hits']['total']
print(f" {'✓' if total > 0 else '✗'} {name}: {total} 条")
except Exception as e:
print(f" ✗ {name}: {e}")
return True
def test_aiops_api(token):
"""2. 通过 AIOps API 测试"""
print("\n" + "=" * 60)
print("2. AIOps API — 数据源管理")
# 列出数据源
result = req('GET', '/api/v1/log-datasources?type=elasticsearch', token=token)
sources = result if isinstance(result, list) else result.get('data', [])
sources = sources if isinstance(sources, list) else []
print(f" ES 数据源: {len(sources)} 个")
es_ds = None
for s in sources:
print(f" - id={s['id']} name={s['name']} url={s['url']}")
if 'monitoring' in s.get('name', '').lower():
es_ds = s
# 测试连接
print("\n 测试连接:")
result = req('POST', '/api/v1/log-datasources/test', {
"type": "elasticsearch", "url": ES_URL
}, token=token)
print(f" {'✓' if result['code'] == 200 else '✗'} {result['message']}")
# ES 查询 — 不带时间范围
print("\n 查询 (无时间范围):")
ds_id = es_ds['id'] if es_ds else 1
result = req('POST', '/api/v1/logs/elasticsearch/query', {
"datasource_id": ds_id, "query": "*", "size": 3
}, token=token)
d = result.get('data', {})
entries = d.get('entries', [])
print(f" total={d.get('total', 0)}, entries={len(entries)}")
# ES 查询 — 带时间范围 (修复后)
print("\n 查询 (带时间范围 — Z→+00:00修复):")
result = req('POST', '/api/v1/logs/elasticsearch/query', {
"datasource_id": ds_id,
"query": "*",
"size": 3,
"start": "2026-06-21T00:00:00.000Z",
"end": "2026-06-28T00:00:00.000Z"
}, token=token)
d = result.get('data', {})
entries = d.get('entries', [])
print(f" total={d.get('total', 0)}, entries={len(entries)}")
if entries:
e = entries[0]
print(f" ts={e.get('timestamp')}")
print(f" msg preview: {e.get('message','')[:120]}")
def test_frontend_mock():
"""3. 前端行为模拟"""
print("\n" + "=" * 60)
print("3. 前端查询面板逻辑分析")
# 模拟前端 isDemo 判断
sources_from_api = [
{"id": 5, "name": "monitoring-es-7", "type": "elasticsearch", "url": "http://10.10.10.156:9200"}
]
demo_sources = [
{"id": -2, "name": "demo-es", "type": "elasticsearch", "url": ""}
]
# 前端合并逻辑
all_sources = sources_from_api + demo_sources
print(" 合并后的数据源列表:")
for s in all_sources:
is_demo = s['id'] < 0 or not s['url']
print(f" - {s['name']} (id={s['id']}) → isDemo={is_demo}")
# 选第一个源 (real)
selected = all_sources[0]
print(f"\n 默认选择: {selected['name']} (id={selected['id']})")
is_demo = selected['id'] < 0 or not selected['url']
print(f" isDemo={is_demo}")
if is_demo:
print(" → 会走模拟数据,不查询真实 ES!")
else:
print(" → 会调用真实 API: POST /api/v1/logs/elasticsearch/query")
print(f" datasource_id={selected['id']}")
if __name__ == "__main__":
token = login()
print(f"AIOps 登录成功\n")
test_direct_es()
test_aiops_api(token)
test_frontend_mock()
print("\n" + "=" * 60)
print("结论:")
print(" 1. ES 直连 .monitoring-es-7-* 有数据 ✓")
print(" 2. timestamp 字段存在,@timestamp 不存在")
print(" 3. Z 时区格式在 ES 7.2 date_time 上失败")
print(" 4. 修复: Z→+00:00 + should[ @timestamp, timestamp ]")
print(" 5. AIOps API 查询已返回数据 ✓")
print(" 6. 前端 isDemo 判断正常,会走真实查询")