feat(bitdefender): complete Network module (build-out 3/N)
- Completed Network module for bitdefender skill (GravityZone Public API) - Added getEndpointTags (read), setEndpointLabel (gated), createReconfigureClientTask/reconfigure (gated) - Confirmed createUninstallTask, getEndpointsByPolicy, getManagedEndpointDetailsByIp, createScanTaskByMailboxes not found under /network - Fixed endpoint-tags renderer to handle list result (previously crashing _print_kv) - raw gates setEndpointLabel; reconfigure already gated - selftest 55 -> 60 passing Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,15 +30,16 @@ Per-module workflow: fetch module doc -> list methods + params -> add to
|
||||
- [x] activateCompany (gated)
|
||||
- [x] deleteCompany (gated)
|
||||
|
||||
## network
|
||||
## network — COMPLETE (all exposed methods)
|
||||
- [x] getNetworkInventoryItems · getEndpointsList · getManagedEndpointDetails
|
||||
- [x] getScanTasksList · createScanTask · moveEndpoints
|
||||
- [x] createCustomGroup · deleteCustomGroup · moveCustomGroup · deleteEndpoint
|
||||
- [x] assignPolicy
|
||||
- [ ] createReconfigureClientTask (gated)
|
||||
- [ ] createUninstallTask (gated)
|
||||
- [ ] setEndpointLabel
|
||||
- [ ] (enumerate remaining: getEndpointsCounts, getManagedEndpointDetails opts, etc.)
|
||||
- [x] createReconfigureClientTask (gated)
|
||||
- [x] setEndpointLabel (gated)
|
||||
- [x] getEndpointTags (read)
|
||||
- [DEAD] createUninstallTask (+variants) — does NOT exist under /network this version
|
||||
- [DEAD] getEndpointsByPolicy / getManagedEndpointDetailsByIp / createScanTaskByMailboxes — not found
|
||||
|
||||
## packages
|
||||
- [x] getPackagesList · createPackage · getInstallationLinks · deletePackage
|
||||
|
||||
@@ -95,10 +95,12 @@ In `getNetworkInventoryItems` results, `type == 1` denotes a company node.
|
||||
| `deleteEndpoint` | `endpointId` | VERIFIED (destructive) | Remove an endpoint from inventory. CLI-gated behind `--confirm`. |
|
||||
| `deleteCustomGroup` | `groupId` | VERIFIED (destructive) | Delete a custom group. CLI-gated behind `--confirm`. |
|
||||
| `moveCustomGroup` | `groupId, newParentId` | VERIFIED | Re-parent a custom group. |
|
||||
| `assignPolicy` | uncertain | UNVERIFIED | Assigns an EXISTING policy to endpoints. Param names not confirmed (likely `policyId` + a targets list). Do NOT use blindly — confirm against archived docs first. `raw` only. |
|
||||
| `createReconfigureClientTask` | uncertain | UNVERIFIED | Param shape not confirmed. `raw` only. |
|
||||
| `createUninstallTask` | uncertain | UNVERIFIED | Destructive; param shape not confirmed. `raw` only. |
|
||||
| `setEndpointLabel` | uncertain | UNVERIFIED | Param shape not confirmed. `raw` only. |
|
||||
| `assignPolicy` | `policyId, targetIds[], forcePolicyInheritance?, inheritFromAbove?` | VERIFIED (docs+probe) | CLI `assign-policy`, gated. See policies section. |
|
||||
| `createReconfigureClientTask` | `targetIds[] (req) + reconfigure body` | VERIFIED (probe) | CLI `reconfigure`, gated. STATE-CHANGING. |
|
||||
| `setEndpointLabel` | `endpointId (req), label (req)` | VERIFIED (probe) | CLI `set-label`, gated. STATE-CHANGING. |
|
||||
| `getEndpointTags` | `{}` | VERIFIED LIVE | List endpoint tags (returns a list). CLI `endpoint-tags`. |
|
||||
| `createUninstallTask` + variants | — | DOES NOT EXIST | No uninstall-task method under `/network` in this API version (createUninstallTask / createUninstallClientTask / createUninstallRoleTask / uninstallClientTask all "method not found"). Uninstall via the console. |
|
||||
| `getEndpointsByPolicy`, `getManagedEndpointDetailsByIp`, `createScanTaskByMailboxes` | — | DOES NOT EXIST | "method not found" on this tenant/version. |
|
||||
|
||||
## packages (`/packages`)
|
||||
|
||||
|
||||
@@ -510,6 +510,38 @@ def cmd_move(client, args):
|
||||
args.json, _print_kv)
|
||||
|
||||
|
||||
def _print_tags(tags) -> None:
|
||||
items = tags if isinstance(tags, list) else (tags or {}).get("items", [])
|
||||
print(f"Endpoint tags: {len(items)}")
|
||||
for t in items:
|
||||
print(f" {t}")
|
||||
|
||||
|
||||
def cmd_endpoint_tags(client, args):
|
||||
_emit(client.get_endpoint_tags(), args.json, _print_tags)
|
||||
|
||||
|
||||
def cmd_set_label(client, args):
|
||||
if not _gated(f"label endpoint {args.endpoint} = '{args.label}'", args.confirm):
|
||||
return 3
|
||||
result = client.set_endpoint_label(args.endpoint, args.label)
|
||||
_emit({"labeled": args.endpoint, "label": args.label, "result": result},
|
||||
args.json, _print_kv)
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_reconfigure(client, args):
|
||||
extra, rc = _load_json_arg(args.extra_json, "extra-json")
|
||||
if rc:
|
||||
return rc
|
||||
if not _gated(f"reconfigure {len(args.targets)} agent(s): {','.join(args.targets)}",
|
||||
args.confirm):
|
||||
return 3
|
||||
result = client.reconfigure_client(args.targets, extra=extra or None)
|
||||
_emit({"reconfigured": args.targets, "result": result}, args.json, _print_kv)
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_make_group(client, args):
|
||||
result = client.create_custom_group(args.name, args.parent)
|
||||
_emit({"createdGroup": args.name, "result": result}, args.json, _print_kv)
|
||||
@@ -524,7 +556,7 @@ DESTRUCTIVE_RAW_PATTERNS = ("delete", "createuninstall", "createremove",
|
||||
"removefromblocklist", "assignpolicy",
|
||||
"setpushevent", "createaccount", "updateaccount",
|
||||
"configurenotif", "createcompany", "suspendcompany",
|
||||
"activatecompany")
|
||||
"activatecompany", "setendpointlabel")
|
||||
|
||||
|
||||
def _is_destructive_method(method: str) -> bool:
|
||||
@@ -770,6 +802,21 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
sp.add_argument("--name", required=True)
|
||||
sp.add_argument("--parent")
|
||||
|
||||
sub.add_parser("endpoint-tags", help="List endpoint tags.", parents=[common])
|
||||
|
||||
sp = sub.add_parser("set-label", help="Set an endpoint's label (gated).",
|
||||
parents=[common])
|
||||
sp.add_argument("--endpoint", required=True, help="endpointId.")
|
||||
sp.add_argument("--label", required=True)
|
||||
sp.add_argument("--confirm", action="store_true")
|
||||
|
||||
sp = sub.add_parser("reconfigure",
|
||||
help="Reconfigure installed agents (gated).", parents=[common])
|
||||
sp.add_argument("--targets", nargs="+", required=True, help="Endpoint ids.")
|
||||
sp.add_argument("--extra-json",
|
||||
help="JSON reconfigure body (modules/roles/scanMode).")
|
||||
sp.add_argument("--confirm", action="store_true")
|
||||
|
||||
sp = sub.add_parser("raw", help="Call any method directly (power use).",
|
||||
parents=[common])
|
||||
sp.add_argument("--module", required=True)
|
||||
@@ -928,6 +975,9 @@ HANDLERS = {
|
||||
"scan": cmd_scan,
|
||||
"move": cmd_move,
|
||||
"make-group": cmd_make_group,
|
||||
"endpoint-tags": cmd_endpoint_tags,
|
||||
"set-label": cmd_set_label,
|
||||
"reconfigure": cmd_reconfigure,
|
||||
"raw": cmd_raw,
|
||||
"delete-endpoint": cmd_delete_endpoint,
|
||||
"delete-package": cmd_delete_package,
|
||||
|
||||
@@ -721,6 +721,33 @@ class GravityZoneClient:
|
||||
params["status"] = status
|
||||
return self._jsonrpc_request("network", "getScanTasksList", params) or {}
|
||||
|
||||
def get_endpoint_tags(self) -> Any:
|
||||
"""List endpoint tags defined in the tenant (network.getEndpointTags). Read."""
|
||||
return self._jsonrpc_request("network", "getEndpointTags", {})
|
||||
|
||||
def set_endpoint_label(self, endpoint_id: str, label: str) -> Any:
|
||||
"""Set an endpoint's label (network.setEndpointLabel). STATE-CHANGING.
|
||||
Requires `endpointId` and `label` (both verified). Gate at the call site."""
|
||||
return self._jsonrpc_request(
|
||||
"network", "setEndpointLabel",
|
||||
{"endpointId": endpoint_id, "label": label},
|
||||
)
|
||||
|
||||
def reconfigure_client(
|
||||
self, target_ids: list[str], extra: Optional[dict] = None
|
||||
) -> Any:
|
||||
"""Reconfigure installed agents (network.createReconfigureClientTask).
|
||||
STATE-CHANGING. Requires `targetIds` (verified); the reconfigure body
|
||||
(which modules/roles/scanMode to change) is documented and passed via
|
||||
`extra`. Gate at the call site behind --confirm.
|
||||
"""
|
||||
params: dict = {"targetIds": target_ids}
|
||||
if extra:
|
||||
params.update(extra)
|
||||
return self._jsonrpc_request(
|
||||
"network", "createReconfigureClientTask", params
|
||||
)
|
||||
|
||||
# ======================================================================
|
||||
# REPORTS (module `/reports`) — VERIFIED LIVE
|
||||
# ======================================================================
|
||||
|
||||
@@ -105,6 +105,13 @@ check("push-set no confirm -> rc3", ["push-set", "--status", "1", "--url", "http
|
||||
check("push-set enable no url -> rc2", ["push-set", "--status", "1", "--confirm"], want_rc=2)
|
||||
check("raw assignPolicy no confirm -> rc3", ["raw", "--module", "network", "--method", "assignPolicy", "--params", "{}"], want_rc=3)
|
||||
|
||||
# --- network completion ---
|
||||
check("endpoint-tags", ["endpoint-tags"], want_rc=0)
|
||||
check("set-label no confirm -> rc3", ["set-label", "--endpoint", "x", "--label", "y"], want_rc=3)
|
||||
check("reconfigure no confirm -> rc3", ["reconfigure", "--targets", "x"], want_rc=3)
|
||||
check("raw reconfigure no confirm -> rc3", ["raw", "--module", "network", "--method", "createReconfigureClientTask", "--params", "{}"], want_rc=3)
|
||||
check("raw setEndpointLabel no confirm -> rc3", ["raw", "--module", "network", "--method", "setEndpointLabel", "--params", "{}"], want_rc=3)
|
||||
|
||||
# --- companies module ---
|
||||
check("company (own, no id)", ["company"], want_rc=0)
|
||||
check("company-create no confirm -> rc3", ["company-create", "--type", "1", "--name", "Test Co"], want_rc=3, out_has="Would")
|
||||
|
||||
Reference in New Issue
Block a user