add relationship fields
This commit is contained in:
@@ -54,6 +54,21 @@ def _range_seconds(value: object) -> int:
|
||||
return 300
|
||||
|
||||
|
||||
def _graylog_fields_from_result(result: dict[str, object]) -> list[dict[str, object]]:
|
||||
content = result.get("result", {}).get("content", []) if isinstance(result.get("result"), dict) else []
|
||||
text = next((item.get("text", "") for item in content if isinstance(item, dict)), "")
|
||||
if not text:
|
||||
return []
|
||||
try:
|
||||
payload = json.loads(text)
|
||||
except json.JSONDecodeError:
|
||||
return []
|
||||
fields = payload.get("fields", payload) if isinstance(payload, dict) else payload
|
||||
if isinstance(fields, dict) and isinstance(fields.get("fields"), list):
|
||||
fields = fields["fields"]
|
||||
return [item for item in fields if isinstance(item, dict)] if isinstance(fields, list) else []
|
||||
|
||||
|
||||
def _stream_coverage(runtime_values: dict[str, object], stream_profiles: dict[str, object], stream_status: dict[str, object], profile_readiness: list[dict[str, object]], stream_titles: dict[str, str]) -> list[dict[str, object]]:
|
||||
configured = [
|
||||
item for item in runtime_values.get("graylog_streams", [])
|
||||
@@ -85,7 +100,7 @@ def _stream_coverage(runtime_values: dict[str, object], stream_profiles: dict[st
|
||||
"profile": _profile_name(stream_id, stream_titles, profile) if profile else "",
|
||||
"profile_ready": bool(profile),
|
||||
"entity_field": ", ".join(getattr(profile, "entity_fields", ()) or (str(getattr(profile, "entity_field", "")),)) if profile else "",
|
||||
"tracked_fields": len(getattr(profile, "categorical_fields", ())) + len(getattr(profile, "numeric_fields", ())) if profile else 0,
|
||||
"tracked_fields": len(getattr(profile, "categorical_fields", ())) + len(getattr(profile, "numeric_fields", ())) + len(getattr(profile, "relationship_fields", ())) if profile else 0,
|
||||
"ready_fields": ready_fields,
|
||||
"total_fields": total_fields,
|
||||
"readiness": f"{ready_fields}/{total_fields}" if total_fields else "0/0",
|
||||
@@ -155,6 +170,8 @@ def build_status(
|
||||
aggregate_events_total = 0
|
||||
client = GraylogMcpClient(url, token, verify_tls=verify_tls)
|
||||
probe_status = client.probe()
|
||||
discovery_store = FieldDiscoveryStore(history_path) if history_path else None
|
||||
catalog_fields_total = 0
|
||||
for stream_config in stream_configs:
|
||||
stream_id = str(stream_config["id"])
|
||||
profile = stream_profiles.get(stream_id)
|
||||
@@ -164,8 +181,15 @@ def build_status(
|
||||
str(getattr(profile, "timestamp_field", "")),
|
||||
*tuple(str(field) for field in getattr(profile, "categorical_fields", ())),
|
||||
*tuple(str(field) for field in getattr(profile, "numeric_fields", ())),
|
||||
*tuple(str(getattr(relation, "left", "")) for relation in getattr(profile, "relationship_fields", ())),
|
||||
*tuple(str(getattr(relation, "right", "")) for relation in getattr(profile, "relationship_fields", ())),
|
||||
) if profile else ()
|
||||
stream_name = str(stream_config.get("title", "") or stream_titles.get(stream_id) or stream_id)
|
||||
if discovery_store:
|
||||
try:
|
||||
catalog_fields_total += discovery_store.ingest_catalog(stream_id, stream_name, _graylog_fields_from_result(client.call_tool("list_fields", {"streams": [stream_id]})))
|
||||
except RuntimeError:
|
||||
pass
|
||||
aggregate_status: dict[str, object] = {}
|
||||
if use_aggregate:
|
||||
aggregate_status = GraylogAggregateSource(client, stream_id, str(runtime_values.get("graylog_query", "*"))).fetch_count(range_seconds=range_seconds, probe_status=probe_status)
|
||||
@@ -199,6 +223,7 @@ def build_status(
|
||||
"sample_limited_streams": len(sample_limited_streams),
|
||||
"truncated_streams": len(truncated_streams),
|
||||
"aggregate_error_streams": len(aggregate_errors),
|
||||
"catalog_fields": catalog_fields_total,
|
||||
"coverage_status": "partial" if partial_streams else "truncated" if truncated_streams else "complete_window",
|
||||
"coverage_warning": " ".join(warnings),
|
||||
}
|
||||
@@ -338,7 +363,7 @@ def build_status(
|
||||
"baseline": {"enabled": bool(baseline), "sources_ready": len(profiles), "training_days": baseline_training_days, "new_events_recorded": baseline_events, "profile_fields_recorded": profile_baseline_fields, "discovery_fields_recorded": discovered_profile_fields, "discovery_cache_events": len(discovery_cache_events), "maintenance": baseline_maintenance, "size_bytes": baseline_maintenance.get("size_bytes", 0) if isinstance(baseline_maintenance, dict) else 0},
|
||||
"capabilities": {"threat_intel": threat_intel_status, "graylog_mcp": mcp_status, "profile_advisor": profile_advisor_status},
|
||||
"configuration": runtime_config,
|
||||
"stream_profiles": [{"stream_id": item.stream_id, "name": _profile_name(item.stream_id, stream_titles, item), "stream_name": _stream_name(item.stream_id, stream_titles, item), "entity_field": item.entity_field, "entity_fields": list(item.entity_fields), "timestamp_field": item.timestamp_field, "categorical_fields": list(item.categorical_fields), "numeric_fields": list(item.numeric_fields), "detectors": item.detectors, "field_weights": item.field_weights} for item in stream_profiles.values()],
|
||||
"stream_profiles": [{"stream_id": item.stream_id, "name": _profile_name(item.stream_id, stream_titles, item), "stream_name": _stream_name(item.stream_id, stream_titles, item), "entity_field": item.entity_field, "entity_fields": list(item.entity_fields), "timestamp_field": item.timestamp_field, "categorical_fields": list(item.categorical_fields), "numeric_fields": list(item.numeric_fields), "detectors": item.detectors, "field_weights": item.field_weights, "relationship_fields": [{"left": relation.left, "right": relation.right, "name": relation.name} for relation in item.relationship_fields]} for item in stream_profiles.values()],
|
||||
"stream_coverage": stream_coverage,
|
||||
"profile_suggestions": profile_suggestions,
|
||||
"profile_readiness": profile_readiness,
|
||||
|
||||
Reference in New Issue
Block a user