feat(cortex-xdr): endpoint_count counts agents below a target build (1.10.0)
agent_version_lt, compared number by number in the script, as the endpoints API does not filter on the agent version either. Folded into the unpushed 1.10.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LaiNEwG2W5Y9fMo4qXcesr
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -135,16 +135,28 @@ def count(filters):
|
|||||||
return int(reply.get("total_count") or 0)
|
return int(reply.get("total_count") or 0)
|
||||||
|
|
||||||
|
|
||||||
def count_operational(filters, wanted):
|
def version(v):
|
||||||
# The endpoints grid does not filter on operational_status, so the endpoints
|
# "8.5.0.12345" -> (8, 5, 0, 12345); a shorter target such as "8.5" reads as
|
||||||
# the other filters match are read 100 at a time — the API's page ceiling —
|
# 8.5.0.0, so it compares against every build of that release line.
|
||||||
# and counted here. Costs one call per hundred endpoints.
|
parts = []
|
||||||
|
for x in str(v or "").strip().split("."):
|
||||||
|
digits = "".join(c for c in x if c.isdigit())
|
||||||
|
parts.append(int(digits) if digits else 0)
|
||||||
|
while len(parts) < 4:
|
||||||
|
parts.append(0)
|
||||||
|
return tuple(parts)
|
||||||
|
|
||||||
|
|
||||||
|
def count_local(filters, keep):
|
||||||
|
# The endpoints grid filters neither on operational_status nor on the agent
|
||||||
|
# version, so the endpoints the other filters match are read 100 at a time —
|
||||||
|
# the API's page ceiling — and counted here. One call per hundred endpoints.
|
||||||
n, start = 0, 0
|
n, start = 0, 0
|
||||||
while True:
|
while True:
|
||||||
request_data = {"search_from": start, "search_to": start + 100, "filters": filters}
|
request_data = {"search_from": start, "search_to": start + 100, "filters": filters}
|
||||||
reply = post("/endpoints/get_endpoint/", request_data).get("reply") or {}
|
reply = post("/endpoints/get_endpoint/", request_data).get("reply") or {}
|
||||||
page = reply.get("endpoints") or []
|
page = reply.get("endpoints") or []
|
||||||
n += sum(1 for e in page if str(e.get("operational_status") or "").upper() in wanted)
|
n += sum(1 for e in page if keep(e))
|
||||||
start += len(page)
|
start += len(page)
|
||||||
if not page or start >= int(reply.get("total_count") or 0):
|
if not page or start >= int(reply.get("total_count") or 0):
|
||||||
return n
|
return n
|
||||||
@@ -153,16 +165,27 @@ def count_operational(filters, wanted):
|
|||||||
def main():
|
def main():
|
||||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||||
filters = build_filters(inputs)
|
filters = build_filters(inputs)
|
||||||
|
result = {"filters": filters}
|
||||||
|
tests = []
|
||||||
wanted = {x.upper() for x in csv(inputs.get("operational_status"))}
|
wanted = {x.upper() for x in csv(inputs.get("operational_status"))}
|
||||||
if wanted:
|
if wanted:
|
||||||
result = {"count": count_operational(filters, wanted), "filters": filters,
|
tests.append(lambda e: str(e.get("operational_status") or "").upper() in wanted)
|
||||||
"operational_status": sorted(wanted)}
|
result["operational_status"] = sorted(wanted)
|
||||||
|
below = str(inputs.get("agent_version_lt") or "").strip()
|
||||||
|
if below:
|
||||||
|
target = version(below)
|
||||||
|
# An endpoint that reports no version is not counted as outdated: it is
|
||||||
|
# unknown, and calling it outdated would inflate the figure.
|
||||||
|
tests.append(lambda e: bool(e.get("agent_version")) and version(e.get("agent_version")) < target)
|
||||||
|
result["agent_version_lt"] = below
|
||||||
|
if tests:
|
||||||
|
result["count"] = count_local(filters, lambda e: all(t(e) for t in tests))
|
||||||
else:
|
else:
|
||||||
result = {"count": count(filters), "filters": filters}
|
result["count"] = count(filters)
|
||||||
# The fleet total is the denominator the filtered count is read against: it
|
# The fleet total is the denominator the filtered count is read against: it
|
||||||
# turns a bare number into a proportion, and makes a filter that matched the
|
# turns a bare number into a proportion, and makes a filter that matched the
|
||||||
# whole estate — because it was too broad — visible instead of plausible.
|
# whole estate — because it was too broad — visible instead of plausible.
|
||||||
result["total"] = count([]) if filters or wanted else result["count"]
|
result["total"] = count([]) if filters or tests else result["count"]
|
||||||
print(json.dumps(result))
|
print(json.dumps(result))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user