#!/usr/bin/env python3
"""
Delete unloaded Perforce clients that have been inactive for a configured
number of weeks using the P4Python API.
This script identifies client workspaces stored in the unload depot whose
last access time exceeds a configured threshold. It then forcibly deletes
those clients using `p4 client -fd -Fd`. Clients are identified via the
`p4 clients -U` command, which lists unloaded client metadata.
"""
import argparse
import logging
import sys
import time
from typing import Iterable, List
from P4 import P4, P4Exception # type: ignore
import sdputils # type: ignore
def parse_args(argv: Iterable[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Delete inactive unloaded clients via P4Python",
)
parser.add_argument(
"instance",
nargs="?",
default="1",
help="SDP instance identifier (default: '1')",
)
return parser.parse_args(list(argv))
def connect_p4(instance: str) -> P4:
utils = sdputils.SDPUtils(instance)
p4 = P4()
p4.port = utils.server
try:
p4.user = utils.p4user # type: ignore[attr-defined]
except AttributeError:
pass
passwd_path = f"/p4/common/config/.p4passwd.p4_{instance}.admin"
try:
with open(passwd_path) as pf:
p4.password = pf.read().strip()
except FileNotFoundError:
pass
p4.connect()
if p4.password:
p4.run_login()
return p4
def get_inactive_unloaded_clients(p4: P4, weeks: int) -> List[str]:
now = int(time.time())
threshold = weeks * 7 * 24 * 60 * 60
try:
clients = p4.run("clients", "-U")
except P4Exception as exc:
logging.error("Failed to list unloaded clients: %s", exc)
return []
result: List[str] = []
for rec in clients:
name = rec.get("client") or rec.get("Client")
access_str = rec.get("Access") or rec.get("access")
if not name or not access_str:
continue
try:
access = int(access_str)
except (ValueError, TypeError):
continue
if now - access >= threshold:
result.append(name)
return result
def delete_clients(p4: P4, clients: List[str]) -> None:
for client in clients:
try:
# -fd (force delete) and -Fd (delete unloaded client)
p4.run("client", "-fd", "-Fd", client)
logging.info("Deleted unloaded client %s", client)
except P4Exception as exc:
logging.error("Failed to delete client %s: %s", client, exc)
def main(argv: Iterable[str] | None = None) -> int:
args = parse_args(argv or sys.argv[1:])
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
try:
p4 = connect_p4(args.instance)
except P4Exception:
return 1
utils = sdputils.SDPUtils(args.instance)
weeks = int(utils.get("deleteweeks"))
clients = get_inactive_unloaded_clients(p4, weeks)
delete_clients(p4, clients)
return 0
if __name__ == "__main__":
sys.exit(main())
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #9 | 32423 | Russell C. Jackson (Rusty) |
Modernize SDP maintenance scripts: security, correctness, and Python 3 - Replace all os.system() and os.popen() calls with subprocess.run() using argument lists to eliminate shell injection vulnerabilities - Fix critical bugs: broken indentation in convert_label_to_autoreload.py, malformed print() in p4lock/p4unlock, wrong variable in isitalabel, format string typo in maintain_user_from_groups - Add p4 property alias and shared SKIP_USERS constant to sdputils.py - Add try/finally with p4.disconnect() to all P4Python scripts - Replace bare except: with specific exception types throughout - Update all shebangs to python3, remove unnecessary __future__ imports - Use context managers for all file handle operations - Replace from subprocess import * with explicit imports |
||
| #8 | 32388 | Russell C. Jackson (Rusty) | Updates using Claude.ai to clean up the code, reduce duplication, enhanace security, and use current standards. | ||
| #7 | 31825 | Russell C. Jackson (Rusty) | Converted to use P4 api and to use only python 3. | ||
| #6 | 30945 | Russell C. Jackson (Rusty) | Correct missing d parameter in client delete. | ||
| #5 | 30843 | Russell C. Jackson (Rusty) | Updated to use parameters for the user and client commands. | ||
| #4 | 28685 | Russell C. Jackson (Rusty) | Correct missing lines. | ||
| #3 | 28684 | Russell C. Jackson (Rusty) | Removed duplicate line. | ||
| #2 | 28683 | Russell C. Jackson (Rusty) | Added deleteweeks variable. | ||
| #1 | 28682 | Russell C. Jackson (Rusty) | New maintenance scripts to clean up the unload depot. |