sync: auto-sync from HOWARD-HOME at 2026-07-03 20:52:38
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-07-03 20:52:38
This commit is contained in:
@@ -1,17 +1,26 @@
|
||||
import json, urllib.request, ssl, os
|
||||
import json, urllib.request, ssl, os, datetime
|
||||
from collections import Counter
|
||||
BASE="https://computerguru.syncromsp.com/api/v1"
|
||||
SK=os.environ["SK"]; RMM=os.environ["RMM"]; TOK=os.environ["TOK"]
|
||||
MAXDAYS=int(os.environ.get("MAXDAYS","31"))
|
||||
ctx=ssl.create_default_context()
|
||||
def get(url,hdr=None):
|
||||
return urllib.request.urlopen(urllib.request.Request(url,headers=hdr or {}),context=ctx,timeout=30).read()
|
||||
def clean(b): return bytes(c for c in b if c>=32 or c in (9,10,13))
|
||||
now=datetime.datetime.now()
|
||||
def lastseen(a):
|
||||
ki=(a.get("properties") or {}).get("kabuto_information") or {}
|
||||
ts=ki.get("last_synced_at")
|
||||
if not ts: return None,None
|
||||
try: d=datetime.datetime.strptime(ts[:19],"%Y-%m-%dT%H:%M:%S")
|
||||
except Exception: return None,None
|
||||
return d,(now-d).days
|
||||
agents=json.loads(clean(get(f"{RMM}/api/agents",{"Authorization":f"Bearer {TOK}"})))
|
||||
by_client={}
|
||||
for a in agents:
|
||||
by_client.setdefault(a.get("client_name") or "",set()).add((a.get("hostname") or "").lower())
|
||||
tgts=json.load(open("projects/gps-rmm-audit/targets.json"))["clients"]
|
||||
folder_ctr=Counter(); body=[]; total=0
|
||||
folder_ctr=Counter(); body=[]; total=0; stale=0; unknown=0
|
||||
for t in tgts:
|
||||
c=t["client"]; cid=t["cid"]
|
||||
try: data=json.loads(clean(get(f"{BASE}/customer_assets?customer_id={cid}&per_page=100&api_key={SK}")))
|
||||
@@ -19,21 +28,26 @@ for t in tgts:
|
||||
body.append(f"**{c}** - ERROR: {e}\n"); continue
|
||||
dev=[a for a in data.get("assets",[]) if a.get("asset_type")=="Syncro Device" and a.get("name")]
|
||||
rmm=by_client.get(c,set())
|
||||
gap=sorted([a for a in dev if a["name"].lower() not in rmm], key=lambda a:a["name"].lower())
|
||||
if not gap: continue
|
||||
total+=len(gap)
|
||||
body.append(f"**{c}** - {len(dev)} managed / {len(rmm)} in RMM / {len(gap)} need install")
|
||||
gap=[a for a in dev if a["name"].lower() not in rmm]
|
||||
keep=[]
|
||||
for a in gap:
|
||||
f=a.get("policy_folder_id") or "-"
|
||||
folder_ctr[str(f)]+=1
|
||||
body.append(f" - {a['name']} (folder {f})")
|
||||
d,days=lastseen(a)
|
||||
if d is None: unknown+=1; continue # no check-in data -> exclude
|
||||
if days>MAXDAYS: stale+=1; continue # offline > MAXDAYS -> exclude
|
||||
keep.append((a,d,days))
|
||||
if not keep: continue
|
||||
keep.sort(key=lambda x:x[0]["name"].lower())
|
||||
total+=len(keep)
|
||||
body.append(f"**{c}** - {len(keep)} need install (of {len(gap)} not-in-RMM; rest offline >{MAXDAYS}d)")
|
||||
for a,d,days in keep:
|
||||
f=a.get("policy_folder_id") or "-"; folder_ctr[str(f)]+=1
|
||||
body.append(f" - {a['name']} (folder {f}, seen {d.strftime('%Y-%m-%d')}, {days}d ago)")
|
||||
body.append("")
|
||||
out=["# GPS clients - Syncro-managed machines NOT in GuruRMM","",
|
||||
"Install ScreenConnect / push agent. Syncro assets (authoritative) vs live GuruRMM, 2026-07-03.",
|
||||
"`folder N` = Syncro policy_folder_id (the ScreenConnect-install policy is tied to a folder - devices in the wrong folder don't get SC).","",
|
||||
"## Folder distribution across the gap machines (spot the SC-install folder vs outliers)"]
|
||||
for f,n in folder_ctr.most_common():
|
||||
out.append(f"- folder {f}: {n} machines")
|
||||
out+=["",f"## TOTAL needing install: {total}",""]+body
|
||||
out=["# GPS clients - Syncro-managed machines NOT in GuruRMM (ACTIVE only)","",
|
||||
f"Machines missing from GuruRMM that checked into Syncro within the last {MAXDAYS} days. Excluded: {stale} offline >{MAXDAYS}d, {unknown} with no check-in data. Snapshot 2026-07-03.",
|
||||
"`folder N` = Syncro policy_folder_id.","",
|
||||
"## Folder distribution (active gap machines)"]
|
||||
for f,n in folder_ctr.most_common(): out.append(f"- folder {f}: {n}")
|
||||
out+=["",f"## TOTAL active needing install: {total}",""]+body
|
||||
open("projects/gps-rmm-audit/needs-screenconnect.md","w",encoding="utf-8").write("\n".join(out))
|
||||
print(f"total={total}, folders={dict(folder_ctr)}")
|
||||
print(f"active={total} excluded_stale={stale} excluded_unknown={unknown}")
|
||||
|
||||
Reference in New Issue
Block a user