sync: auto-sync from HOWARD-HOME at 2026-07-06 16:41:20
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-07-06 16:41:20
This commit is contained in:
@@ -101,18 +101,37 @@ def _gated(action_desc: str, confirm: bool) -> bool:
|
||||
|
||||
# --- handlers ---
|
||||
def cmd_status(client, args):
|
||||
# Auth check via the fleet filter; report instance + custom-property map + counts.
|
||||
access = client.list_sessions(session_type="access")
|
||||
support = client.list_sessions(session_type="support")
|
||||
na = len(access) if isinstance(access, list) else 0
|
||||
ns = len(support) if isinstance(support, list) else 0
|
||||
# Auth check via the most basic verified method (GetSessionsByName), so status
|
||||
# confirms credentials even on an extension version without GetSessionsByFilter.
|
||||
client.get_sessions_by_name("")
|
||||
print(f"[OK] Authenticated to {SC_BASE_URL}")
|
||||
print(f" extension: {client.extension_guid}")
|
||||
print(f" custom properties: {CUSTOM_PROPERTIES}")
|
||||
print(f" fleet: {na} Access (unattended) + {ns} Support session(s)")
|
||||
# Fleet counts are a best-effort add-on: the filter method may be absent, and it
|
||||
# must never fail the auth check that status exists to perform.
|
||||
try:
|
||||
access = client.list_sessions(session_type="access")
|
||||
support = client.list_sessions(session_type="support")
|
||||
na = len(access) if isinstance(access, list) else 0
|
||||
ns = len(support) if isinstance(support, list) else 0
|
||||
print(f" fleet: {na} Access (unattended) + {ns} Support session(s)")
|
||||
except ScreenConnectError as exc:
|
||||
print(f" fleet: (listing unavailable via GetSessionsByFilter: {exc})")
|
||||
return 0
|
||||
|
||||
|
||||
def _effective_type(args) -> str:
|
||||
"""Resolve the SessionType filter for `sessions`. Explicit --type wins. Otherwise
|
||||
a bare listing defaults to the Access fleet, but a TARGETED search (by name/like/
|
||||
company/site/tag) spans all types - matching the old GetSessionsByName behavior,
|
||||
where a by-name lookup found Support sessions too."""
|
||||
if args.type is not None:
|
||||
return args.type
|
||||
if any([args.name, args.like, args.company, args.site, args.tag]):
|
||||
return "all"
|
||||
return "access"
|
||||
|
||||
|
||||
def cmd_sessions(client, args):
|
||||
# Full-fleet listing is the default (no selector -> every Access session).
|
||||
# A raw --filter wins; otherwise build a filter from the structured flags.
|
||||
@@ -120,20 +139,17 @@ def cmd_sessions(client, args):
|
||||
data = client.get_sessions_by_filter(args.filter)
|
||||
else:
|
||||
data = client.list_sessions(
|
||||
session_type=args.type,
|
||||
session_type=_effective_type(args),
|
||||
name=args.name or None,
|
||||
name_like=args.like,
|
||||
company=args.company,
|
||||
site=args.site,
|
||||
tag=args.tag,
|
||||
)
|
||||
if isinstance(data, list) and args.limit and len(data) > args.limit:
|
||||
shown = data[: args.limit]
|
||||
if args.json:
|
||||
_emit(shown, True)
|
||||
else:
|
||||
_print_sessions(shown)
|
||||
print(f" ... {len(data) - args.limit} more (raise --limit or use --json)")
|
||||
# --limit only caps the human-readable print; --json is always the full set.
|
||||
if not args.json and isinstance(data, list) and args.limit and len(data) > args.limit:
|
||||
_print_sessions(data[: args.limit])
|
||||
print(f" ... {len(data) - args.limit} more (raise --limit or use --json)")
|
||||
return 0
|
||||
_emit(data, True) if args.json else _print_sessions(data)
|
||||
return 0
|
||||
@@ -225,8 +241,9 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
sp.add_argument("--company", help="Filter by CP1 = Company.")
|
||||
sp.add_argument("--site", help="Filter by CP2 = Site.")
|
||||
sp.add_argument("--tag", help="Filter by CP3 = Tag.")
|
||||
sp.add_argument("--type", default="access",
|
||||
help="access (default) | support | meeting | all.")
|
||||
sp.add_argument("--type", default=None,
|
||||
help="access | support | meeting | all. Default: access for a "
|
||||
"bare listing, all when a name/company/etc selector is given.")
|
||||
sp.add_argument("--filter", help="Raw session-filter expression (overrides the flags).")
|
||||
sp.add_argument("--limit", type=int, default=0,
|
||||
help="Cap rows printed (0 = no cap; --json is always full).")
|
||||
|
||||
@@ -230,7 +230,9 @@ class ScreenConnectClient:
|
||||
@staticmethod
|
||||
def _filter_literal(value: str) -> str:
|
||||
"""Single-quote a value for the session-filter language, escaping any
|
||||
embedded single quote by doubling it (SQL-style)."""
|
||||
embedded single quote by doubling it. VERIFIED LIVE 2026-07-06:
|
||||
`CustomProperty1 LIKE '*Andy''s*'` matched the "Andy's Mobile Fuel" session,
|
||||
so doubling is the correct escape for this filter grammar."""
|
||||
return "'" + str(value).replace("'", "''") + "'"
|
||||
|
||||
def build_session_filter(
|
||||
@@ -249,7 +251,12 @@ class ScreenConnectClient:
|
||||
CP1/CP2/CP3, `extra` is a raw clause appended verbatim. Defaults to the whole
|
||||
Access fleet when nothing else is specified."""
|
||||
clauses: list[str] = []
|
||||
if session_type and session_type.lower() != "all":
|
||||
if session_type and session_type.lower() == "all":
|
||||
# 'all' = every session type. Only Access + Support exist on this
|
||||
# instance; an OR clause is the verified match-all (a bare '*' matches
|
||||
# nothing). VERIFIED LIVE 2026-07-06 -> 982 = 958 Access + 24 Support.
|
||||
clauses.append("(SessionType = 'Access' OR SessionType = 'Support')")
|
||||
elif session_type:
|
||||
literal = SESSION_TYPE_LITERALS.get(session_type.lower(), session_type)
|
||||
clauses.append(f"SessionType = {self._filter_literal(literal)}")
|
||||
if name:
|
||||
|
||||
@@ -27,31 +27,40 @@ def run(args):
|
||||
return p.returncode, p.stdout, p.stderr
|
||||
|
||||
|
||||
def check(name, args, *, want_rc=None, out_has=None, out_json_ok=False):
|
||||
def check(name, args, *, want_rc=None, out_has=None, out_json_ok=False, out_json_min_len=None):
|
||||
rc, out, err = run(args)
|
||||
problems = []
|
||||
if want_rc is not None and rc != want_rc:
|
||||
problems.append(f"rc={rc} want {want_rc}")
|
||||
if out_has and out_has not in out:
|
||||
problems.append(f"stdout missing {out_has!r}")
|
||||
if out_json_ok:
|
||||
if out_json_ok or out_json_min_len is not None:
|
||||
try:
|
||||
json.loads(out)
|
||||
parsed = json.loads(out)
|
||||
if out_json_min_len is not None and len(parsed) < out_json_min_len:
|
||||
problems.append(f"json len {len(parsed)} < {out_json_min_len}")
|
||||
except Exception as e:
|
||||
problems.append(f"stdout not JSON: {e}")
|
||||
results.append(("PASS" if not problems else "FAIL", name, "; ".join(problems)))
|
||||
|
||||
|
||||
# --- reads: succeed (rc 0) [hit the live instance] ---
|
||||
check("status", ["status"], want_rc=0, out_has="fleet:")
|
||||
check("status", ["status"], want_rc=0, out_has="Authenticated")
|
||||
check("status shows fleet", ["status"], want_rc=0, out_has="fleet:")
|
||||
check("sessions (full fleet)", ["sessions"], want_rc=0, out_has="Sessions:")
|
||||
check("sessions json", ["sessions", "--json"], want_rc=0, out_json_ok=True)
|
||||
check("sessions --limit", ["sessions", "--limit", "5"], want_rc=0, out_has="Sessions:")
|
||||
check("sessions --like", ["sessions", "--like", "DELL", "--json"], want_rc=0, out_json_ok=True)
|
||||
check("sessions --type all", ["sessions", "--type", "all", "--json"], want_rc=0, out_json_ok=True)
|
||||
check("sessions --filter", ["sessions", "--filter", "SessionType = 'Access'", "--json"],
|
||||
want_rc=0, out_json_ok=True)
|
||||
check("sessions --company", ["sessions", "--company", "Safesite", "--json"],
|
||||
want_rc=0, out_json_ok=True)
|
||||
# --limit must NOT truncate --json (documented "--json is always full" contract):
|
||||
# Safesite has 62 machines; even with --limit 1 the JSON must return them all.
|
||||
check("sessions --limit does not truncate json",
|
||||
["sessions", "--company", "Safesite", "--limit", "1", "--json"],
|
||||
want_rc=0, out_json_min_len=10)
|
||||
|
||||
# --- build-installer: pure URL build (no network) ---
|
||||
check("build-installer", ["build-installer", "--name", "HOST", "--company", "AZ Computer Guru",
|
||||
|
||||
Reference in New Issue
Block a user