This document describes all the maintenance scripts included in the Server Deployment Package (SDP). These scripts are located in the $SDP/Maintenance directory and provide tools for routine Perforce server administration tasks.
Most maintenance scripts read their configuration from maintenance.cfg in the Maintenance directory. Key configuration parameters include:
| Parameter | Description |
|---|---|
weeks |
Default inactivity threshold in weeks |
deleteweeks |
Threshold for permanent deletion |
userweeks |
Inactivity threshold for user accounts |
weeks1warn |
First warning threshold |
weeks2warn |
Second warning threshold |
administrator |
Email from address |
mailhost |
SMTP server hostname |
mailport |
SMTP server port |
mailsecure |
Use TLS (1 or 0) |
mailuser |
SMTP username |
mailpass |
SMTP password |
default_user_password |
Default password for new users |
Generate lists of inactive Perforce specs (branches, clients, labels, users) that haven't been accessed within a specified number of weeks.
python accessdates.py [instance]
Output Files:
branches.txtclients.txtlabels.txtusers.txtCreate Perforce users in bulk from a CSV file.
python createusers.py <userlist.csv> [instance]
CSV Format:
user,email,fullname
jsmith,[email protected],John Smith
Remove users that haven't accessed Perforce within the configured threshold.
python delusers.py [instance]
Uses the weeks threshold from maintenance.cfg.
Remove Perforce users and their associated metadata (clients, shelves).
python p4deleteuser.py [instance] <user_or_file> [--simulate]
Arguments:
instance: SDP instance identifier (default: '1')user_or_file: Username or file containing usernames (one per line)--simulate: Print actions without executingFeatures:
p4 user -FDy commandSet a user's password to the default password configured in maintenance.cfg.
python setpass.py [instance] <username>
Find group members without corresponding user accounts (orphaned members).
python checkusers.py [instance]
Output: removeusersfromgroups.txt
Find users that are not members of any Perforce group.
python checkusers_not_in_group.py [instance]
Sync users based on group membership - creates missing users and removes users not in any group.
python maintain_user_from_groups.py [instance]
Send warning emails to users whose accounts are scheduled for deletion due to inactivity.
python email_pending_user_deletes.py [instance]
Uses userweeks threshold from maintenance.cfg.
Generate a CSV report of Perforce users across multiple servers.
python totalusers.py [cfgfile] [--output <outfile>] [--verbose]
Configuration File (totalusers.cfg):
case_sensitive=1
ROBOT,p4robot
ROBOT,jenkins
SERVER,server1.example.com:1666,p4admin,1
SERVER,server2.example.com:1666,p4admin,2
Create Perforce groups from a configuration file.
python creategroups.py [instance]
Input File Format (groups.txt):
group,groupname1
username1
username2
group,groupname2
username3
Add user(s) to a Perforce group.
python addusertogroup.py [instance] <user_or_file> <group>
Examples:
python addusertogroup.py jsmith developers
python addusertogroup.py 2 userlist.txt developers
Remove user(s) from all their Perforce groups.
python removeuserfromgroups.py [instance] <user_or_file>
Remove users from a specific list of groups.
python removeuserfromgroups_file.py <instance> <user_or_file> <groups_file>
Remove users from a specific group.
python removeusersfromgroup.py [instance] <user_or_file> <groupname>
Mirror user access from one user to others (add target users to all groups the source user belongs to).
python mirroraccess.py <instance> <source_user> <target_user> [<user3> ... <userN>]
Send quarterly audit reminders to group owners asking them to validate group membership.
python group_audit.py [instance]
Unload inactive Perforce clients to the unload depot.
python unload_clients.py [instance]
Uses weeks threshold from maintenance.cfg. Skips Swarm clients.
Unload inactive clients, but forcibly delete those with exclusive file locks (which can't be unloaded).
python unload_clients_with_delete.py [instance]
Delete inactive unloaded clients from the unload depot.
python delete_unload_clients.py [instance]
Uses deleteweeks threshold from maintenance.cfg.
Generate lists of inactive unloaded clients and labels.
python unloadaccessdates.py [instance]
Output Files:
unload_clients.txtunload_labels.txtSend warning emails for inactive clients scheduled for unload.
python email_pending_client_deletes.py [instance]
Sends two-tier warnings using weeks1warn and weeks2warn thresholds.
Delete shelves and inactive clients.
python del_shelve.py [instance]
Unload labels that haven't been accessed within the configured threshold.
python unload_labels.py [instance]
Convert Perforce labels from noautoreload to autoreload mode.
python convert_label_to_autoreload.py [instance] <label_or_file>
Check if a label exists in Perforce.
python isitalabel.py [instance] <labelname>
Exit Codes:
0: Label exists1: Label not foundLock a Perforce label.
python p4lock.py [instance] <labelname>
Unlock a Perforce label.
python p4unlock.py [instance] <labelname>
Note: Saves owner info to
owner.txtfor restoration byp4lock.py.
Send email to all Perforce users.
./email.sh "Subject line"
Prerequisites:
message.txt must exist containing the email bodypymail.py must be availableSend email from command line using SMTP.
python pymail.py -t <to-address or file> -s <subject> -i <input-file> [instance]
Arguments:
-t: Recipient email address or path to file with addresses-s: Email subject-i: Path to file containing email bodyExamples:
python pymail.py -t [email protected] -s "Test" -i message.txt
python pymail.py -t recipients.txt -s "Announcement" -i body.txt 2
Find groups in the protection table that don't exist on the server.
# First, extract groups from protect table
p4 protect -o | grep group | cut -d " " -f 3 | sort | uniq > protect_groups.txt
# Then find non-existent groups
python protect_groups.py [instance] protect_groups.txt > remove_groups.txt
Remove groups from the Perforce protection table.
# Generate protection table
p4 protect -o > p4.protect
# Clean up invalid groups
python clean_protect.py remove_groups.txt p4.protect
# Review and apply
p4 protect -i < new.p4.protect
Count total files and revisions from a Perforce files list.
p4 files //... > files.txt
python countrevs.py files.txt
Find proxy servers in Perforce server log.
python proxysearch.py <logfile>
Output: Unique proxy IP addresses (one per line)
Generate a combined file list from all Perforce depots.
./create_p4_filelist.sh
Output: p4_file_list.txt
Iterates through depots one directory level deep to avoid memory spikes.
Copy a directory tree to a target with lowercase names.
cd /p4/1/depots
python lowercp.py depot
Note: Configure
TARGETDEPOTPATHvariable at top of script.
Rename a directory tree to all lowercase (in place).
python lowertree.py <directory>
Useful for converting from case-sensitive to case-insensitive Perforce servers.
Delete all empty pending changelists.
python remove_empty_pending_changes.py [instance]
Note: Changelists with
#do-not-deletein description are protected.
Remove Perforce jobs and their associated fixes.
p4 jobs > jobs.txt
python remove_jobs.py [instance] jobs.txt
Force update on changelists (useful with form triggers).
p4 changes | cut -d " " -f 2 > changes.txt
python update-changes.py [instance] changes.txt
Core utility module used by most other maintenance scripts. Provides:
SDPUtils class for Perforce interactionsmaintenance.cfgp4 command execution via run_p4()Example Usage:
from sdputils import SDPUtils
utils = SDPUtils("1")
utils.login()
users = utils.get_all_users()
| Category | Script Count |
|---|---|
| User Management | 10 |
| Group Management | 7 |
| Client Management | 6 |
| Label Management | 5 |
| Email/Notification | 3 |
| Protection Table | 2 |
| Reporting | 2 |
| File/Archive Operations | 5 |
| Utility | 1 |
| Total | 41 |
# 1. Generate list of inactive users
python accessdates.py 1
# 2. Review users.txt, then delete
python p4deleteuser.py 1 users.txt --simulate # Review first
python p4deleteuser.py 1 users.txt # Execute
# 1. Send warnings to users
python email_pending_client_deletes.py 1
# 2. Unload inactive clients
python unload_clients.py 1
# 3. Later, delete unloaded clients
python delete_unload_clients.py 1
# 1. Extract groups from protect table
p4 protect -o | grep group | cut -d " " -f 3 | sort | uniq > protect_groups.txt
# 2. Find non-existent groups
python protect_groups.py 1 protect_groups.txt > remove_groups.txt
# 3. Export current protect table
p4 protect -o > p4.protect
# 4. Remove invalid groups
python clean_protect.py remove_groups.txt p4.protect
# 5. Review and apply
diff p4.protect new.p4.protect
p4 protect -i < new.p4.protect
# Find users in groups that don't exist
python checkusers.py 1
# Review and remove orphaned entries
python removeuserfromgroups.py 1 removeusersfromgroups.txt# SDP Maintenance Scripts Reference This document describes all the maintenance scripts included in the Server Deployment Package (SDP). These scripts are located in the `$SDP/Maintenance` directory and provide tools for routine Perforce server administration tasks. --- ## Table of Contents - [Configuration](#configuration) - [User Management Scripts](#user-management-scripts) - [Group Management Scripts](#group-management-scripts) - [Client Management Scripts](#client-management-scripts) - [Label Management Scripts](#label-management-scripts) - [Email and Notification Scripts](#email-and-notification-scripts) - [Protection Table Scripts](#protection-table-scripts) - [Reporting Scripts](#reporting-scripts) - [File and Archive Operations](#file-and-archive-operations) - [Utility Scripts](#utility-scripts) --- ## Configuration Most maintenance scripts read their configuration from `maintenance.cfg` in the Maintenance directory. Key configuration parameters include: | Parameter | Description | |-----------|-------------| | `weeks` | Default inactivity threshold in weeks | | `deleteweeks` | Threshold for permanent deletion | | `userweeks` | Inactivity threshold for user accounts | | `weeks1warn` | First warning threshold | | `weeks2warn` | Second warning threshold | | `administrator` | Email from address | | `mailhost` | SMTP server hostname | | `mailport` | SMTP server port | | `mailsecure` | Use TLS (1 or 0) | | `mailuser` | SMTP username | | `mailpass` | SMTP password | | `default_user_password` | Default password for new users | --- ## User Management Scripts ### accessdates.py Generate lists of inactive Perforce specs (branches, clients, labels, users) that haven't been accessed within a specified number of weeks. ```bash python accessdates.py [instance] ``` **Output Files:** - `branches.txt` - `clients.txt` - `labels.txt` - `users.txt` --- ### createusers.py Create Perforce users in bulk from a CSV file. ```bash python createusers.py <userlist.csv> [instance] ``` **CSV Format:** ```csv user,email,fullname jsmith,[email protected],John Smith ``` --- ### delusers.py Remove users that haven't accessed Perforce within the configured threshold. ```bash python delusers.py [instance] ``` Uses the `weeks` threshold from `maintenance.cfg`. --- ### p4deleteuser.py Remove Perforce users and their associated metadata (clients, shelves). ```bash python p4deleteuser.py [instance] <user_or_file> [--simulate] ``` **Arguments:** - `instance`: SDP instance identifier (default: '1') - `user_or_file`: Username or file containing usernames (one per line) - `--simulate`: Print actions without executing **Features:** - Protects service accounts (p4admin, perforce, swarm) from deletion - Supports batch deletion from files - Uses `p4 user -FDy` command --- ### setpass.py Set a user's password to the default password configured in `maintenance.cfg`. ```bash python setpass.py [instance] <username> ``` --- ### checkusers.py Find group members without corresponding user accounts (orphaned members). ```bash python checkusers.py [instance] ``` **Output:** `removeusersfromgroups.txt` --- ### checkusers_not_in_group.py Find users that are not members of any Perforce group. ```bash python checkusers_not_in_group.py [instance] ``` --- ### maintain_user_from_groups.py Sync users based on group membership - creates missing users and removes users not in any group. ```bash python maintain_user_from_groups.py [instance] ``` --- ### email_pending_user_deletes.py Send warning emails to users whose accounts are scheduled for deletion due to inactivity. ```bash python email_pending_user_deletes.py [instance] ``` Uses `userweeks` threshold from `maintenance.cfg`. --- ### totalusers.py Generate a CSV report of Perforce users across multiple servers. ```bash python totalusers.py [cfgfile] [--output <outfile>] [--verbose] ``` **Configuration File (totalusers.cfg):** ``` case_sensitive=1 ROBOT,p4robot ROBOT,jenkins SERVER,server1.example.com:1666,p4admin,1 SERVER,server2.example.com:1666,p4admin,2 ``` --- ## Group Management Scripts ### creategroups.py Create Perforce groups from a configuration file. ```bash python creategroups.py [instance] ``` **Input File Format (groups.txt):** ``` group,groupname1 username1 username2 group,groupname2 username3 ``` --- ### addusertogroup.py Add user(s) to a Perforce group. ```bash python addusertogroup.py [instance] <user_or_file> <group> ``` **Examples:** ```bash python addusertogroup.py jsmith developers python addusertogroup.py 2 userlist.txt developers ``` --- ### removeuserfromgroups.py Remove user(s) from all their Perforce groups. ```bash python removeuserfromgroups.py [instance] <user_or_file> ``` --- ### removeuserfromgroups_file.py Remove users from a specific list of groups. ```bash python removeuserfromgroups_file.py <instance> <user_or_file> <groups_file> ``` --- ### removeusersfromgroup.py Remove users from a specific group. ```bash python removeusersfromgroup.py [instance] <user_or_file> <groupname> ``` --- ### mirroraccess.py Mirror user access from one user to others (add target users to all groups the source user belongs to). ```bash python mirroraccess.py <instance> <source_user> <target_user> [<user3> ... <userN>] ``` --- ### group_audit.py Send quarterly audit reminders to group owners asking them to validate group membership. ```bash python group_audit.py [instance] ``` --- ## Client Management Scripts ### unload_clients.py Unload inactive Perforce clients to the unload depot. ```bash python unload_clients.py [instance] ``` Uses `weeks` threshold from `maintenance.cfg`. Skips Swarm clients. --- ### unload_clients_with_delete.py Unload inactive clients, but forcibly delete those with exclusive file locks (which can't be unloaded). ```bash python unload_clients_with_delete.py [instance] ``` --- ### delete_unload_clients.py Delete inactive unloaded clients from the unload depot. ```bash python delete_unload_clients.py [instance] ``` Uses `deleteweeks` threshold from `maintenance.cfg`. --- ### unloadaccessdates.py Generate lists of inactive unloaded clients and labels. ```bash python unloadaccessdates.py [instance] ``` **Output Files:** - `unload_clients.txt` - `unload_labels.txt` --- ### email_pending_client_deletes.py Send warning emails for inactive clients scheduled for unload. ```bash python email_pending_client_deletes.py [instance] ``` Sends two-tier warnings using `weeks1warn` and `weeks2warn` thresholds. --- ### del_shelve.py Delete shelves and inactive clients. ```bash python del_shelve.py [instance] ``` --- ## Label Management Scripts ### unload_labels.py Unload labels that haven't been accessed within the configured threshold. ```bash python unload_labels.py [instance] ``` --- ### convert_label_to_autoreload.py Convert Perforce labels from `noautoreload` to `autoreload` mode. ```bash python convert_label_to_autoreload.py [instance] <label_or_file> ``` --- ### isitalabel.py Check if a label exists in Perforce. ```bash python isitalabel.py [instance] <labelname> ``` **Exit Codes:** - `0`: Label exists - `1`: Label not found --- ### p4lock.py Lock a Perforce label. ```bash python p4lock.py [instance] <labelname> ``` --- ### p4unlock.py Unlock a Perforce label. ```bash python p4unlock.py [instance] <labelname> ``` > **Note:** Saves owner info to `owner.txt` for restoration by `p4lock.py`. --- ## Email and Notification Scripts ### email.sh Send email to all Perforce users. ```bash ./email.sh "Subject line" ``` **Prerequisites:** - `message.txt` must exist containing the email body - `pymail.py` must be available --- ### pymail.py Send email from command line using SMTP. ```bash python pymail.py -t <to-address or file> -s <subject> -i <input-file> [instance] ``` **Arguments:** - `-t`: Recipient email address or path to file with addresses - `-s`: Email subject - `-i`: Path to file containing email body **Examples:** ```bash python pymail.py -t [email protected] -s "Test" -i message.txt python pymail.py -t recipients.txt -s "Announcement" -i body.txt 2 ``` --- ## Protection Table Scripts ### protect_groups.py Find groups in the protection table that don't exist on the server. ```bash # First, extract groups from protect table p4 protect -o | grep group | cut -d " " -f 3 | sort | uniq > protect_groups.txt # Then find non-existent groups python protect_groups.py [instance] protect_groups.txt > remove_groups.txt ``` --- ### clean_protect.py Remove groups from the Perforce protection table. ```bash # Generate protection table p4 protect -o > p4.protect # Clean up invalid groups python clean_protect.py remove_groups.txt p4.protect # Review and apply p4 protect -i < new.p4.protect ``` --- ## Reporting Scripts ### countrevs.py Count total files and revisions from a Perforce files list. ```bash p4 files //... > files.txt python countrevs.py files.txt ``` --- ### proxysearch.py Find proxy servers in Perforce server log. ```bash python proxysearch.py <logfile> ``` **Output:** Unique proxy IP addresses (one per line) --- ## File and Archive Operations ### create_p4_filelist.sh Generate a combined file list from all Perforce depots. ```bash ./create_p4_filelist.sh ``` **Output:** `p4_file_list.txt` Iterates through depots one directory level deep to avoid memory spikes. --- ### lowercp.py Copy a directory tree to a target with lowercase names. ```bash cd /p4/1/depots python lowercp.py depot ``` > **Note:** Configure `TARGETDEPOTPATH` variable at top of script. --- ### lowertree.py Rename a directory tree to all lowercase (in place). ```bash python lowertree.py <directory> ``` Useful for converting from case-sensitive to case-insensitive Perforce servers. --- ### remove_empty_pending_changes.py Delete all empty pending changelists. ```bash python remove_empty_pending_changes.py [instance] ``` > **Note:** Changelists with `#do-not-delete` in description are protected. --- ### remove_jobs.py Remove Perforce jobs and their associated fixes. ```bash p4 jobs > jobs.txt python remove_jobs.py [instance] jobs.txt ``` --- ### update-changes.py Force update on changelists (useful with form triggers). ```bash p4 changes | cut -d " " -f 2 > changes.txt python update-changes.py [instance] changes.txt ``` --- ## Utility Scripts ### sdputils.py Core utility module used by most other maintenance scripts. Provides: - `SDPUtils` class for Perforce interactions - Configuration loading from `maintenance.cfg` - Safe `p4` command execution via `run_p4()` - Secure password management - Common Perforce operations (get users, groups, etc.) **Example Usage:** ```python from sdputils import SDPUtils utils = SDPUtils("1") utils.login() users = utils.get_all_users() ``` --- # Summary | Category | Script Count | |----------|--------------| | User Management | 10 | | Group Management | 7 | | Client Management | 6 | | Label Management | 5 | | Email/Notification | 3 | | Protection Table | 2 | | Reporting | 2 | | File/Archive Operations | 5 | | Utility | 1 | | **Total** | **41** | --- # Common Workflows ## Remove Inactive Users ```bash # 1. Generate list of inactive users python accessdates.py 1 # 2. Review users.txt, then delete python p4deleteuser.py 1 users.txt --simulate # Review first python p4deleteuser.py 1 users.txt # Execute ``` ## Clean Up Inactive Clients ```bash # 1. Send warnings to users python email_pending_client_deletes.py 1 # 2. Unload inactive clients python unload_clients.py 1 # 3. Later, delete unloaded clients python delete_unload_clients.py 1 ``` ## Clean Up Protection Table ```bash # 1. Extract groups from protect table p4 protect -o | grep group | cut -d " " -f 3 | sort | uniq > protect_groups.txt # 2. Find non-existent groups python protect_groups.py 1 protect_groups.txt > remove_groups.txt # 3. Export current protect table p4 protect -o > p4.protect # 4. Remove invalid groups python clean_protect.py remove_groups.txt p4.protect # 5. Review and apply diff p4.protect new.p4.protect p4 protect -i < new.p4.protect ``` ## Audit Group Membership ```bash # Find users in groups that don't exist python checkusers.py 1 # Review and remove orphaned entries python removeuserfromgroups.py 1 removeusersfromgroups.txt ```
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 32388 | Russell C. Jackson (Rusty) | Updates using Claude.ai to clean up the code, reduce duplication, enhanace security, and use current standards. |