Administering a QCFractal server with PortalClient#
This page shows how to administer a running QCFractal server programmatically using the Python client API provided by qcportal’s PortalClient.
The examples below assume you can connect to the server with an administrative account (or a user granted the appropriate permissions for the action).
Note
These administrative APIs correspond to the same capabilities exposed by the command-line interface described in Server administration via the CLI but are accessible from Python for scripting and automation.
Getting started: connecting and authentication#
from qcportal import PortalClient
# Connect with an admin-capable account
client = PortalClient(
"http://localhost:7777",
username="admin",
password="your-admin-password",
)
Users: add, list, enable/disable, change passwords, delete#
User objects are represented by UserInfo.
For what users, roles, and groups are - the list of roles and what each permits, what happens on a server with security disabled, and how unauthenticated access works - see Users, Roles, and Groups. This section covers the administrative operations.
Note
Roles are not scoped by ownership. A user with the submit role may modify or delete
any record or dataset on the server, not only the ones they created.
List users and view details#
from qcportal.auth import UserInfo
# List all users
users = client.list_users()
for u in users:
print(u.username, u.role, u.enabled, u.groups)
# Get a specific user (by username or id)
alice = client.get_user("alice")
print(alice)
Add a new user#
When creating a user you provide a UserInfo (without id) and optionally an initial password. If you omit the password, the server will generate one and return it.
from qcportal.auth import UserInfo
# Minimal required fields: username, role, enabled
new_user = UserInfo(
username="alice",
role="submit", # see the list of roles above
groups=["chemistry"], # optional group memberships
enabled=True,
fullname="Alice Example",
email="alice@example.org",
)
# Let the server generate a password
generated_pw = client.add_user(new_user)
print("Generated password:", generated_pw)
# Or specify an initial password yourself
client.add_user(new_user, password="ChangeMe123")
Enable/disable a user or update fields#
Use modify_user with a UserInfo that includes the user’s id (you can obtain it via get_user).
# Disable a user account
bob = client.get_user("bob")
bob.enabled = False
bob = client.modify_user(bob)
print("Bob enabled?", bob.enabled)
# Change role or groups
bob.role = "maintain"
bob.groups = sorted(set(bob.groups + ["theory"]))
bob = client.modify_user(bob)
Change a user’s password#
Admins can change any user’s password. Users can also change their own password by omitting the username.
# Admin changes a user's password (returns the new password)
new_pw = client.change_user_password("alice", new_password="BetterPW!1")
# Ask the server to generate a random password
random_pw = client.change_user_password("alice")
# User changes their own password (assumes logged in as alice)
# client = PortalClient(..., username="alice", password="old")
# new_pw = client.change_user_password(new_password="my-new-secret")
Delete a user#
client.delete_user("alice")
Groups: create, list, get, delete#
Groups are described by GroupInfo and can be used
for organization and authorization policies.
List and get groups#
from qcportal.auth import GroupInfo
groups = client.list_groups()
for g in groups:
print(g.groupname, g.description)
chem = client.get_group("chemistry")
print(chem)
Create and delete groups#
from qcportal.auth import GroupInfo
new_group = GroupInfo(groupname="chemistry", description="Chemistry users")
client.add_group(new_group)
# Remove a group (users will have it removed from their memberships)
client.delete_group("chemistry")
Note
Adding/removing users to groups is done by updating the user’s groups field
via modify_user().
Compute managers: viewing manager information#
You can query compute manager registrations and see their status and capabilities.
# List all managers (iterator yields ComputeManager models)
it = client.query_managers(limit=100)
managers = list(it)
for m in managers:
print(m.name, m.status, m.cluster, m.hostname, m.compute_tags)
# Filter by name or status. Managers are either 'active' or 'inactive'
for m in client.query_managers(name=["my_manager"], status=["active"]):
print(m)
# Ask which managers could handle a task with certain tags/programs
possible = client.query_active_managers(
compute_tag=["cpu-ephemeral", "bigmem"],
programs={"psi4": ["1.8"], "torchani": ["2.2"]},
)
print("Candidate managers:", possible)
Access log: view and clear#
The server access log tracks who accessed which API, and when.
Query access logs#
from datetime import timedelta
from qcportal.utils import now_at_utc
# Recent entries for a specific user
one_day_ago = now_at_utc() - timedelta(days=1)
for entry in client.query_access_log(user=["alice"], after=one_day_ago, limit=100):
print(entry.timestamp, entry.user, entry.module, entry.method, entry.full_uri)
# Summarize accesses per day
summary = client.query_access_summary(group_by="day", after=one_day_ago)
for day, entries in summary.entries.items():
print(day, sum(x.count for x in entries))
Delete old access logs#
Danger
Deleting log entries is permanent. Consider exporting before deletion.
from datetime import timedelta
from qcportal.utils import now_at_utc
cutoff = now_at_utc() - timedelta(days=30)
deleted = client.delete_access_log(before=cutoff)
print("Deleted entries:", deleted)
Internal jobs: view, cancel, delete#
Internal jobs are server-maintained background jobs. You can query their status, request cancellation, or delete completed/error entries.
from qcportal.internal_jobs import InternalJobStatusEnum
# Query internal jobs (iterator)
jobs = list(client.query_internal_jobs(status=[InternalJobStatusEnum.running], limit=50))
for j in jobs:
print(j.id, j.name, j.status, j.modified_on)
# Get a specific job
job = client.get_internal_job(job_id=123)
print(job)
# Cancel a job (asks server to stop it)
client.cancel_internal_job(job_id=123)
# Delete a finished/failed job entry
client.delete_internal_job(job_id=123)
Tips and notes#
Permissions: Some actions require elevated privileges. Ensure the account you authenticate with has the necessary role/policies on the server.
Validation: Helper functions like
is_valid_username(),is_valid_password(), andis_valid_groupname()validate inputs and are invoked by client methods. Errors will be raised if inputs are invalid.Pagination: Many query methods return iterators (eg, managers, access logs, internal jobs). Iterate or cast to
listto pull results.