Source code for accre.database.client.parent_accounts

"""
Mixin class for databse client with parent account related
functions
"""
from datetime import datetime

from accre.database.model import *
from accre.database.util import limit_and_offset


[docs]class DBClientParentAccountMixin: """ Functionality related to slurm parent accounts """
[docs] def add_account(self, *, name, department, division, organization, pis, scheduler_account=True, fairshare=1, max_cpu=None, max_mem=None, max_runmins=None, qos='normal', active=True, join_date=None ): """ Add a new administrative account to the database. The dept/div/org triplet must be a valid combination, i.e. the division must be a member of the organization. :param str name: The name of the account :param str department: Name of the department, must be a department already in the database :param str division: Name of the division, must be a division already in the database :param str organization: Name of the organization, must be an organization already in the database :param list(str) pis: A list of PI vunetids. Typically one PI. Can be an empty list for an account with no PI. :param bool scheduler_account: True if this account should exist on the cluster job scheduler :param int fairshare: Scheduler fairshare for the account :param int max_cpu: Maximum running CPU cores for the account :param str max_mem: Maximum running memory for the account, should be of the form "100G" or "10800M" :param int max_runmins: Maximum running scheduled cpu minutes for the account :param str qos: Scheduler QOS for the account :param bool active: True for accounts currently using the cluster or being billed for services :param datetime.datetime join_date: Date that the PI joined the cluster, defaults to the current date """ if join_date is None: join_date = datetime.now() with self.engine.begin() as conn: ins = ACCOUNTS.insert().values( name=name, department=department, division=division, organization=organization, scheduler_account=scheduler_account, fairshare=fairshare, max_cpu=max_cpu, max_mem=max_mem, max_runmins=max_runmins, qos=qos, active=active, join_date=join_date ) conn.execute(ins) for pi in pis: ins = ACCOUNTS_PIS.insert().values( name=name, pi=pi, ) conn.execute(ins) ins = ACCOUNT_LOGS.insert().values( name=name, date=join_date, comment="Account added to database with PIs: {}".format(','.join(pis)) ) conn.execute(ins)
[docs] def add_account_log(self, name, comment, log_date=None): """ Add an entry to the given business account's log. :param str name: name of the account :param str comment: entry to add to the log :param datetime.datetime log_date: Date to use for the log entry, defaults to the current date. """ if log_date is None: log_date = datetime.now() with self.engine.connect() as conn: ins = ACCOUNT_LOGS.insert().values( name=name, date=log_date, comment=comment ) conn.execute(ins)
[docs] def get_account_logs(self, name): """ Fetch user logs for a given business account sorted by newest first :param str name: business account name :return: list of all account log dates and comments for the account :rtype: list(tuple(datetime.datetime, comment)) """ with self.engine.connect() as conn: s = select([ACCOUNT_LOGS.c.date, ACCOUNT_LOGS.c.comment]).where( ACCOUNT_LOGS.c.name == name ) result = list(conn.execute(s)) return sorted(result, reverse=True)
[docs] def list_accounts(self, active=False): """ List all accounts in the databse, or all active accounts. :param bool active: List only active accounts if set to true :returns: List of account ids :rtype: list(str) """ with self.engine.connect() as conn: if active: s = select([ACCOUNTS.c.name, ACCOUNTS.c.active]).where( ACCOUNTS.c.active == True ) else: s = select([ACCOUNTS.c.name]) return [r['name'] for r in conn.execute(s).fetchall()]
[docs] def all_accounts_info(self, limit=None, offset=None): """ Retrieve general information for all ACCRE adminstrative accounts NOT including PIs associated with the account. :param int limit: Maximum number of records to return, return all if None :param int offset: Offset from first record to begin list of records, start at the first record if None :returns: General account information for all accounts :rtype: list(dict) """ with self.engine.connect() as conn: s = select([ACCOUNTS]) s = limit_and_offset(s, limit=limit, offset=offset) accounts = conn.execute(s).fetchall() return [dict(a) for a in accounts]
[docs] def account_info(self, name): """ Retrieve general information for an ACCRE adminstrative account including all PI VUNetIDs :param str name: ACCRE account to be queried :returns: General account information :rtype: dict """ with self.engine.connect() as conn: s = select([ACCOUNTS]).where(ACCOUNTS.c.name == name) account = conn.execute(s).fetchone() s = select([ACCOUNTS_PIS]).where(ACCOUNTS_PIS.c.name == name) pis = [u['pi'] for u in conn.execute(s).fetchall()] s = select([GROUPS]).where(name == GROUPS.c.account) groups = [u['name'] for u in conn.execute(s).fetchall()] result = dict(account) result['pis'] = pis result['groups'] = groups return result
[docs] def modify_account(self, account, reason=None, log_date=None, **updates): """ Modify one or more fields of an existing ACCRE account and log the change with an optional reason. :param str account: Name of the account :param str reason: Explanation of the change to be logged :param dict updates: Columns to be updated with their values :param datetime.datetime log_date: Date to log the user modification, defaults to the current date """ if log_date is None: log_date = datetime.now() invalid_fields = [ c for c in updates if c not in ACCOUNTS.columns or c == 'name' ] if invalid_fields: raise ACCREValueError( 'Invalid account fields for update: {}'.format(invalid_fields) ) with self.engine.begin() as conn: up = ACCOUNTS.update().values(**updates).where( ACCOUNTS.c.name == account ) conn.execute(up) comment = 'Updated account fields: {0}.'.format( ', '.join(updates.keys()) ) if reason: comment = comment + ' Reason: {0}'.format(reason) ins = ACCOUNT_LOGS.insert().values( name=account, date=log_date, comment=comment ) conn.execute(ins)
[docs] def add_account_pis(self, name, *pis, reason=None, log_date=None): """ Add specified pis to a business account :param str name: business account to add pi(s) to :param str pis: pis to add to the account :param str reason: explanation of the change for logging :param datetime.datetime log_date: Date to use for the log entry, defaults to the current date. """ if log_date is None: log_date = datetime.now() with self.engine.begin() as conn: for pi in pis: ins = ACCOUNTS_PIS.insert().values( name=name, pi=pi ) conn.execute(ins) msg = "Pi(s) {0} added to account".format(', '.join(pis)) if reason: msg += ", reason: {0}".format(reason) ins = ACCOUNT_LOGS.insert().values( name=name, date=log_date, comment=msg ) conn.execute(ins)
[docs] def remove_account_pis(self, name, *pis, reason=None, log_date=None): """ Remove specified pis from a business account :param str name: business account remove add pi(s) from :param str pis: pis to remove from the account :param str reason: explanation of the change for logging :param datetime.datetime log_date: Date to use for the log entry, defaults to the current date. """ if log_date is None: log_date = datetime.now() with self.engine.begin() as conn: for pi in pis: delete = ACCOUNTS_PIS.delete().where( (ACCOUNTS_PIS.c.name == name) & (ACCOUNTS_PIS.c.pi == pi) ) conn.execute(delete) msg = "Pi(s) {0} removed from account".format(', '.join(pis)) if reason: msg += ", reason: {0}".format(reason) ins = ACCOUNT_LOGS.insert().values( name=name, date=log_date, comment=msg ) conn.execute(ins)