#!/bin/env python """ This file is a customized version for LAP of the usit common-code version. The customizations affect all MAS related issues : the functions : add_remote_user_to_GOLD (modified) user_is_in_MAS_DB (removed) _get_MAS_projects (removed) _get_MAS_username (removed) """ import os, sys, logging, threading, time, string from sqlalchemy import * import subprocess import re from pprint import * import smtplib from smtplib import SMTPException import Project_managers import os ## needed to sort complex data structures from operator import itemgetter, attrgetter ## ==== ACTIVATE THE DB ENGINE variable GOLDDB defined in startup_settings.sh ===== #application_db_engine = create_engine(os.environ['GOLDDB'], encoding='utf-8') #metadata = MetaData(application_db_engine) def associate_users_to_projects ( emails, project) : """ Used by PIs to associate users to the projects in GOLD which belong to the PI." """ message = '' ## Get the account id get_account_id_command = "sudo /opt/gold/bin/glsaccount --show Id -n %s" % project p = subprocess.Popen(get_account_id_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() account_id = '' account_info = [] for line in p.stdout.readlines(): account_info = line.split() ## The last line of the output account_id = account_info[0] ## Process the users for email in emails : associate_user_to_project_command = "sudo /opt/gold/bin/gchproject --addUser %s %s" % (email, project) p = subprocess.Popen(associate_user_to_project_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully",line) : message += "User %s associated successfully to project %s .
" % (email,project) print "User %s associated successfully to project %s!" % (email,project) else : message += "Failed to associate user %s to project %s .
" % (email,project) message += line status = 'error' return (message,status) ## Add user to account. This account is shared account by all project members add_to_account_command = "sudo /opt/gold/bin/gchaccount --addUser %s %s" % (email,account_id ) p = subprocess.Popen(add_to_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully created",line) : message = message + "User %s added to account %s in project %s.
" % (email,project,project) print "Added to account in %s for user %s." % (project,email) status = 'done' return (message,status) def add_remote_user_to_GOLD( email, feide_username, idp ) : """ At registration all users are added to GOLD: User is inserted to GOLD user DB and to gx_default (Galaxy default) project in GOLD. This is a new function for the common code only!! """ message = "" username = email user_info = [] description = 'Unspecified IdP' print "========== Accounting.py IDP =========", idp ## Add the user to GOLD DB if re.search("test-fe.cbu.uib.no", idp ): description = 'NELS IdP user' elif re.search("feide.no", idp ): description = 'FEIDE IdP user' useradd_command = "sudo /opt/gold/bin/gmkuser %s -d \"%s\"" % (username,description) p = subprocess.Popen(useradd_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() ## Do not proceed if the user exists!! for line in p.stdout.readlines(): if re.search("User already exists",line) : message = "User %s already exists in the GOLD DB!" % username return message ## Check if user has been added to GOLD DB user_check_command = "sudo /opt/gold/bin/glsuser -u %s " % username p = subprocess.Popen(user_check_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search(username,line) : user_info = line.split() ## Nikolay USIT - LAP customization ## Check if the user is associated with a MAS project ## projects = _get_MAS_projects( feide_username) projects = [] ## If the user is sucessfully created if user_info[0] == username and user_info[1] == 'True' : ## If the user is member of MAS projects and no 200 CPU hrs quota is allowed if len(projects) > 0 : proj_names = " ".join(projects) message = "A remote user %s has been added to the portal.
The user is a member of the project(s) %s and can only run jobs in these projects.\n" % (username,proj_names) #print "Feide user %s added successfully! User associated to Notur projects." % username return message else : ## Add user to default galaxy project gx_default, create account and credit the account with default CPU hours add_to_gx_default_command = "sudo /opt/gold/bin/gchproject --addUsers %s gx_default " % username p = subprocess.Popen(add_to_gx_default_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully",line) : message = "Remote user %s added successfully to GOLD DB and the default portal project (gx_galaxy) only.\n" % username #print "Feide user %s added successfully to GOLD DB and the default portal project (gx_galaxy) only." % username ## Add user to account 'username_gx_default' in project gx_default create_account_command = "sudo /opt/gold/bin/gmkaccount -p gx_default -u %s -n \"%s_gx_default\"" % (username,username) p = subprocess.Popen(create_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully created",line) : message = message + "Created account in default portal project (gx_galaxy) for remote user %s. \n" % username #print "Created account in gx_default for remote user %s." % username #print line ## Credit the account - 200 CPU hours ## Get the account id get_account_id_command = "sudo /opt/gold/bin/glsaccount --show Id -n %s_gx_default" % username p = subprocess.Popen(get_account_id_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() account_id = '' account_info = [] for line in p.stdout.readlines(): account_info = line.split() account_id = account_info[0] ## Credit the account (in hours) credit_account_command = "sudo /opt/gold/bin/gdeposit -h -a %s -z 10000" % account_id p = subprocess.Popen(credit_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully deposited",line) : message = message + "Credited account %s_gx_default for remote user %s in default portal project (gx_galaxy).\n" % (username,username) message = message + line #print "Credited account in gx_default for remote user %s." % username return message else : print "Failed to create a user in GOLD" def add_feide_user_to_GOLD( email, feide_username ) : """ Deprecated, use add_remote_user_to GOLD instead!!! At registration all feide users are added to GOLD: User is inserted to GOLD user DB and to gx_default (Galaxy default) project in GOLD. This function presumes that a check has been already performed and the user is _NOT_ in the galaxy user DB """ #username = email #user_info = [] ### Add the user to GOLD DB #useradd_command = "sudo /opt/gold/bin/gmkuser %s -d \"Default FEIDE-Galaxy user\"" % username #p = subprocess.Popen(useradd_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() ### Check if user has been added to GOLD DB #user_check_command = "sudo /opt/gold/bin/glsuser -u %s " % username #p = subprocess.Popen(user_check_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() #for line in p.stdout.readlines(): #if re.search(username,line) : #user_info = line.split() ### Check if the user is associated with a MAS project #projects = _get_MAS_projects( feide_username) ### If the user is sucessfully created #if user_info[0] == username and user_info[1] == 'True' : ### If the user is member of MAS projects and no 200 CPU hrs quota is allowed #if len(projects) > 0 : #proj_names = " ".join(projects) #message = "
FEIDE user %s has been added to the Lifeportal.
The user is a member of the project(s) %s and can only run jobs in these projects.
" % (username,proj_names) #print "Feide user %s added successfully! User associated to Notur projects." % username #return message #else : ### Add user to default galaxy project gx_default, create account and credit the account with default CPU hours #add_to_gx_default_command = "sudo /opt/gold/bin/gchproject --addUsers %s gx_default " % username #p = subprocess.Popen(add_to_gx_default_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() #for line in p.stdout.readlines(): #if re.search("Successfully",line) : #message = "
Feide user %s added successfully to GOLD DB and the default lifeportal project (gx_galaxy) only.
" % username #print "Feide user %s added successfully to GOLD DB and the default lifeportal project (gx_galaxy) only." % username ### Add user to account 'username_gx_default' in project gx_default #create_account_command = "sudo /opt/gold/bin/gmkaccount -p gx_default -u %s -n \"%s_gx_default\"" % (username,username) #p = subprocess.Popen(create_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() #for line in p.stdout.readlines(): #if re.search("Successfully created",line) : #message = message + "Created account in default lifeportal project (gx_galaxy) for Feide user %s.
" % username #print "Created account in gx_default for Feide user %s." % username #print line ### Credit the account - 200 CPU hours ### Get the account id #get_account_id_command = "sudo /opt/gold/bin/glsaccount --show Id -n %s_gx_default" % username #p = subprocess.Popen(get_account_id_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() #account_id = '' #account_info = [] #for line in p.stdout.readlines(): #account_info = line.split() #account_id = account_info[0] ### Credit the account (in hours) #credit_account_command = "sudo /opt/gold/bin/gdeposit -h -a %s -z 200" % account_id #p = subprocess.Popen(credit_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() #for line in p.stdout.readlines(): #if re.search("Successfully deposited",line) : #message = message + "Credited account %s_gx_default for Feide user %s in default lifeportal project (gx_galaxy).
" % (username,username) #message = message + line #print "Credited account in gx_default for Feide user %s." % username #return message #else : #print "Failed to create a user in GOLD" def add_notur_non_feide_user_to_GOLD (email) : """ Not implemented function Automatically adds non-feide Notur users to GOLD. No PI action required. Called when "User > Register" tab has been used. """ ### Check and reformat email / username if necessary #message = '' #description = '' #username = email #mas_username = _get_MAS_username ( email ) #email_username = email.split('@')[0] ### This is just a warning!! #if mas_username != email_username : #description = "Notur non-feide user with username and email prefix mismatch - username %s " % (mas_username) #print "username and email prefix don't match!" #else : #description = "Notur non-feide user" ### Add the user to GOLD DB #useradd_command = "sudo /opt/gold/bin/gmkuser %s -d \"%s\"" % (username,description) #p = subprocess.Popen(useradd_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() ### Check if user has been added to GOLD DB #user_check_command = "sudo /opt/gold/bin/glsuser -u %s " % username #p = subprocess.Popen(user_check_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #for line in p.stdout.readlines(): #if re.search(username,line) : #user_info = line.split() ### If the user is sucessfully created #if user_info[0] == username and user_info[1] == 'True' : ## Add to MAS default Galaxy project #add_to_project_command = "sudo /opt/gold/bin/gchproject --addUser %s MAS" % (username) #p = subprocess.Popen(add_to_project_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) #p.wait() #for line in p.stdout.readlines(): #if re.search("Successfully",line) : #message = "
Notur non-feide user %s added successfully to GOLD DB and the default Notur project MAS.
" % (username) #print "Notur non-feide user %s added successfully to GOLD DB and the default Notur project " % username #return message def add_non_feide_user_to_GOLD (email, project, creator = None) : """ Adds non-feide users to GOLD. Called by the PI (project administrator). The user receives an email notification to register in Galaxy """ username = email message = '' ## Add the user to GOLD DB useradd_command = "sudo /opt/gold/bin/gmkuser %s -d \"non-feide user added by %s\"" % (username, creator) p = subprocess.Popen(useradd_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() ## Check if user has been added to GOLD DB user_check_command = "sudo /opt/gold/bin/glsuser -u %s " % username p = subprocess.Popen(user_check_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in p.stdout.readlines(): if re.search(username,line) : user_info = line.split() ## If the user is sucessfully created if user_info[0] == username and user_info[1] == 'True' : # Add to local Galaxy project owned by PI if project : ## Add user to the selected galaxy project and create account add_to_project_command = "sudo /opt/gold/bin/gchproject --addUsers %s %s " % (username, project) p = subprocess.Popen(add_to_project_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully",line) : message = "
Non-feide user %s added successfully to GOLD DB and the project %s.
" % (username,project) print "Non-feide user %s added successfully to GOLD DB and the selected galaxy project" % username ## Add user to the account of the project. ## ## Get the account id get_account_id_command = "sudo /opt/gold/bin/glsaccount --show Id -n %s " % (project) p = subprocess.Popen(get_account_id_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() account_id = '' account_info = [] for line in p.stdout.readlines(): account_info = line.split() account_id = account_info[0] ## Add user to account. This account is shared account by all project members add_to_account_command = "sudo /opt/gold/bin/gchaccount --addUser %s %s" % (username,account_id ) p = subprocess.Popen(add_to_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully created",line) : message = message + "Non-feide user %s added to account %s in project %s.
" % (username,project,project) print "Added to account in %s for Non-feide user %s." % (project,username) # Send notification email Galaxy project sender = 'noreply@usit.uio.no' receiver = username replyto = creator header = 'To:' + receiver + '\nFrom: ' + sender + '\nReply-to: ' + creator + '\nSubject:You have been added to a Lifeportal project' email_msg = header + '\nYou have been added to the Lifeportal project : ' + project +'. Please log in to https://lifeportal.uio.no and register into Lifeportal. You shall chose for username exactly the same email address (' + receiver + ') as the one used in this message!!!\nBest regards,\n' + creator print "=== EMAIL notification message ===\n",email_msg try: smtpObj = smtplib.SMTP('localhost') smtpObj.sendmail(sender, receiver, email_msg) print "Successfully sent email" except SMTPException: print "Error: unable to send email" return message def get_owned_GOLD_projects ( username ) : """ Selects the GOLD projects owned/created by the user calling the function """ get_projects_command = "sudo /opt/gold/bin/glsproject --show Name,Organization | grep %s " % username p = subprocess.Popen(get_projects_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() projects = [] for line in p.stdout.readlines(): project_line = line.split() projects.append(project_line[0]) print "Accounting : I own the following GOLD projects ",projects return projects def get_other_managed_GOLD_projects ( username ) : """ Selects the GOLD projects like BIR and CLOTU. This function is only called from project_admin_grid_base.mako iff the user is a Project Administrator """ get_projects_command = "sudo /opt/gold/bin/glsproject --show Name,Description" p = subprocess.Popen(get_projects_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() projects = [] for line in p.stdout.readlines(): project_line = line.split() if re.search( "CLOTU", project_line[1]) or re.search( "BIR", project_line[1]) : projects.append(project_line[0]) print "Accounting : I am allowed to manage also the following GOLD projects ",projects return projects def list_owned_GOLD_projects ( username ) : """ Lists the GOLD projects, users, descriptions of owned/created by the user calling the function """ get_projects_command = "sudo /opt/gold/bin/glsproject --show Organization,Name,Users,Active,Description | grep %s " % username p = subprocess.Popen(get_projects_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() projects = [] for line in p.stdout.readlines(): project_line = line.split() if project_line[0] == username : ## Join the description in one cell project_line[4:] = [" ".join(project_line[4:])] ## Replace the comma with newline in users list project_line[2] = project_line[2].replace(",","
") ## Remove the owner project_line = project_line[1:] ## Get the project account info project_name = project_line[0] get_account_info_command = "sudo /opt/gold/bin/glsaccount -h --show Amount,Projects | grep -w %s | uniq " % project_name p = subprocess.Popen(get_account_info_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() amount = '' for line in p.stdout.readlines(): account_info = line.split() amount = account_info[0] project_line[3:3] = [amount] ## The project info is now collected projects.append(project_line) projects_sorted = sorted(projects, key=itemgetter(0)) print "Admin is True : Accounting : I own the following GOLD projects ", projects_sorted return projects_sorted def list_all_GOLD_projects (filter_by_project_name = None) : """ Lists all GOLD projects or one defined in filter_by_project_name """ if filter_by_project_name : get_projects_command = "sudo /opt/gold/bin/glsproject --raw --show Organization,Name,Users,Active,Description -p %s" % filter_by_project_name else : get_projects_command = "sudo /opt/gold/bin/glsproject --raw --show Organization,Name,Users,Active,Description" p = subprocess.Popen(get_projects_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out = p.communicate()[0] output = out.split("\n") # print "Accounting OUTPUT \n", output # replace this function by p.communicate() everywhere where big data is envisaged!! #p.wait() projects = [] for line in output: ## skip all lines not containing relevant info if not re.search("@",line) : continue if re.search("root@",line) : continue project_line = line.split('|') #FOR DEBUGGING leave here - Nikolay #print "Accounting AFTER SPLIT PROJECT LINE ", project_line ## Replace the comma with newline in users list project_line[2] = project_line[2].replace(",","
") ## Get the amount available project_name = project_line[1] get_account_info_command = "sudo /opt/gold/bin/gbalance -h --show Name,Available | grep -w %s | uniq " % project_name p = subprocess.Popen(get_account_info_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() amount = '' account_id = '' for line in p.stdout.readlines(): account_info = line.split() amount = account_info[1] project_line[4:4] = [amount] ## Get start/end date start_date = '' end_date = '' get_start_end_date_command = "sudo /opt/gold/bin/glsalloc --show Id,StartTime,EndTime -p %s | sort -n " % project_name p = subprocess.Popen(get_start_end_date_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search('^\d',line) : alloc_info = line.split() start_date = alloc_info[1] end_date = alloc_info[2] project_line.append(start_date) project_line.append(end_date) ## The project info is now collected projects.append(project_line) projects_sorted = sorted(projects, key=itemgetter(0)) print "Admin is True : Accounting : All GOLD projects ", projects_sorted return projects_sorted def get_member_of_GOLD_projects ( username ) : """ Selects the GOLD projects the user is member of """ get_projects_command = "sudo /opt/gold/bin/glsproject --show Name,Users | grep %s " % username p = subprocess.Popen(get_projects_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() projects = [] for line in p.stdout.readlines(): project_line = line.split() if project_line[0] == 'MAS' : continue else : projects.append(project_line[0]) print "Accounting : I am member of the following GOLD projects ", projects return projects def user_is_in_GOLD_DB ( email ) : """ Checks if the user in in the GOLD DB """ username = email user_info = [] user_check_command = "sudo /opt/gold/bin/glsuser -u %s " % username p = subprocess.Popen(user_check_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in p.stdout.readlines(): if re.search(username,line) : user_info = line.split() if user_info and user_info[0] == username and user_info[1] == 'True' : return True else : return False ### Nikolay USIT - Comment for LAP - LAP does not mount /cluster #def user_is_in_MAS_DB ( email ) : #""" #Checks if the user in in the MAS DB, username is an email #""" #username = email #projects = _get_MAS_projects (username) #if len(projects) > 0 : #return True #else : #return False ### Nikolay USIT - Comment for LAP - LAP does not mount /cluster #def _get_MAS_projects ( email ): #""" #Gets the MAS projects the user is member of (email is an email if non-feide, or e.g. user@uio.no if feide) #""" ### Get the real username from MAS #username_mas = _get_MAS_username ( email ) #print "Accounting : real MAS (Notur) username ", username_mas #username_open = '<'+username_mas+'>' #username_close = '' #f=open('/cluster/var/user-info', 'r') #takeline = False #projectline = '' #projects = [] #for line in f : #if re.search( username_open, line) : #takeline = True #if takeline == True and not re.search( username_open , line) and not re.search( username_close, line) : #if re.search('projects', line) : #projectline = line.split() #if len(projectline) == 2 and len(projectline[1]) > 0 : #full_project_list = projectline[1].split(',') #for p in full_project_list : #if p != 'uio' : #projects.append(p) #if re.search(username_close, line): #break #f.close() #print "Accounting : I am member of the following MAS (Notur) projects ", projects #return projects ## Comment for LAP - LAP does not mount /cluster #def _get_MAS_username (email ) : #""" #Gets the MAS correct username : #Necessary check in case the MAS email prefix and the MAS username don't match, #e.g. uname = 'pr2f2815' <> email = 'dimitry.pokhotelov@fmi.fi' #""" #username = email #uname = '' #email = '' ### Get the correct username #f=open('/cluster/var/user-info', 'r') #for line in f : #if re.search( "uname", line) : #uname = line.split()[1] #if re.search( "status", line) : #status = line.split()[1] #if re.search( "email", line) and line.split()[0] == "email": #email = line.split()[1] ### entire Notur email == the entire Galaxy email (e.g. username = email = 'dimitry.pokhotelov@fmi.fi') #if email == username and status == 'open': #username = uname #break ### email prefix in Galaxy == Notur username ( the Galaxy prefix is actually the FEIDE username prefix : nikolaiv@uio.no) #else : #email_prefix = username.split('@')[0] #if email_prefix == uname and status == 'open': #username = uname #break #f.close() #return username def _generate_project_name() : """ Generates a local galaxy project name, e.g. lp45 """ project_list = [] projects = list_all_GOLD_projects() ## Increment if projects exist if projects and len(projects) > 0 : for project in projects : if re.match('^lp\d+',project[1]) : project_list.append(project[1]) def atoi(text): return int(text) if text.isdigit() else text def natural_keys(text): return [ atoi(c) for c in re.split('(\d+)', text) ] project_list.sort(key=natural_keys) last_project = re.split('(\d+)', project_list[-1]) digit = int(last_project[1])+1 generated_project_name = "lp" + str(digit) ## This is the first project! else : generated_project_name = "lp1" print "Generated project name ", generated_project_name return generated_project_name def get_GOLD_project_info( project_name ) : """ Displays the output of glsproject command """ project_info = [] project_info_display_command = "sudo /opt/gold/bin/glsproject --show Name,Users,Description %s " % project_name p = subprocess.Popen(project_info_display_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): info_line = line project_info = info_line.split() ## Replace the comma with newline in users list project_info[1] = project_info[1].replace(",","
") ## Join the description in one cell project_info[2:] = [" ".join(project_info[2:])] if project_info : return project_info def get_GOLD_project_usage( project_name,start_date,end_date ) : """ Displays the output of gusage command """ project_usage = '' project_usage_command = "sudo /opt/gold/bin/gusage -p %s -s %s -e %s" % (project_name,start_date,end_date) p = subprocess.Popen(project_usage_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): line = line.replace("#","") project_usage += line + "
" if project_usage : return project_usage def get_gx_default_project_usage( username ) : """ Displays the output of gusage command """ gx_project_usage = '' #gx_project_usage_command = "sudo /opt/gold/bin/gbalance --show Available -h -p gx_default -u %s" % ( username ) gx_project_usage_command = "sudo /opt/gold/bin/gbalance --show Available,Name -h | grep %s_gx_default | awk '{print $1;}'" % ( username ) p = subprocess.Popen(gx_project_usage_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): gx_project_usage = line if gx_project_usage : return gx_project_usage def add_project_to_GOLD( email, project_name, cpu_amount, gold_project_description, start_date, end_date) : """ Adds a project to the GOLD DB. The user running the function is the owner of the project (email is the username). Owner is encoded in "Organization" """ print "owner EMAIL ", email message = '' ## Create ownership : "Organization" contains the owner (by email). GOLD is responsible for duplicates. create_organization_command = "sudo /opt/gold/bin/goldsh Organization Create Name=\'%s\' " % email p = subprocess.Popen(create_organization_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() ## For debugging purposes only. GOLD skips the creation request if Organization exists for line in p.stdout.readlines(): if re.search("Successfully",line) : print "Organization %s created " % email break else : print line ## Create the project itself create_project_command = "sudo /opt/gold/bin/gmkproject -d \"%s\" %s -u MEMBERS --createAccount=False -o %s " % ( gold_project_description, project_name, email) p = subprocess.Popen(create_project_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() ## For debugging for line in p.stdout.readlines(): if re.search("Successfully",line) : message = "Succesfully created project : %s" % project_name else : message = line ## Create account create_account_command = "sudo /opt/gold/bin/gmkaccount -p %s -n \"%s\" -u MEMBERS -d \"account for %s project \"" % ( project_name, project_name, project_name ) p = subprocess.Popen(create_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully created",line) : message = message + "
Created account for project %s .
" % project_name print "Created account for project %s." % project_name else : message = line ## Credit the account (in hours) credit_account_command = "sudo /opt/gold/bin/gdeposit -h -s %s -e %s -z %s -p %s " % (start_date, end_date, cpu_amount, project_name) p = subprocess.Popen(credit_account_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): print "Line from gdeposit in add_project_to_GOLD " if re.search("Successfully deposited",line) : message = message + "Credited account %s in project %s .
" % (project_name, project_name) print "Credited account in project %s with amount %s hours " % (project_name, cpu_amount) return message def deactivate_project( project_name ) : """ Deactivates the project/account definitively from GOLD DB. Can be activated """ deactivate_project_command = "sudo /opt/gold/bin/gchproject -I -p %s" % project_name p = subprocess.Popen(deactivate_project_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully",line) : message = "Succesfully deactivated project : %s" % project_name status = 'done' break else : message = line status = 'error' return (message,status) def activate_project( project_name ) : """ Deactivates the project/account definitively from GOLD DB. Can be activated """ activate_project_command = "sudo /opt/gold/bin/gchproject -A -p %s" % project_name p = subprocess.Popen(activate_project_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully",line) : message = "Succesfully activated project : %s" % project_name status = 'done' break else : message = line status = 'error' return (message,status) def modify_project( project_name, cpu_amount, start_date, end_date) : """ Changes the cpu_hours amount (allocation) and start/end dates of an already existing project account. Can only be used for local galaxy projects which have 1 account per project. DO NOT USE FOR gx_default (default feide users projects) Called from "Modify" projects button """ message = '' status = '' print "cpu amount", cpu_amount if not re.match('^-',cpu_amount): ## deposit in hours modify_command = "sudo /opt/gold/bin/gdeposit -h -p %s -s %s -e %s -z %s" % (project_name, start_date, end_date, cpu_amount) p = subprocess.Popen(modify_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully deposited",line) : message = message + "Modified project %s .
" % project_name message = message + line status = 'done' print "Credited account in project %s with cpu_amount %s hours " % (project_name, cpu_amount) break else : message += line + "
" status = 'error' print "Accounting : GOLD Error line credit account >>>", line else : ## withdraw a = re.split('-',cpu_amount) cpu_amount = a[1] modify_command = "sudo /opt/gold/bin/gwithdraw -h -p %s -z %s" % (project_name, cpu_amount) p = subprocess.Popen(modify_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully withdrew",line) : message = message + "Withdrew %s cpu_hours from project %s. " % (cpu_amount, project_name) status = 'done' print "Withdrew %s cpu_hours from project %s" % (cpu_amount, project_name) break else : message += line + "
" status = 'error' print "Accounting : GOLD Error line withdraw >>>", line return (message,status) def _slurmtimesecs (elapsed_time) : """ Converts time into seconds """ multipliers4 = [86400,3600,60,1] multipliers3 = [3600,60,1] seconds = None d_h_m_s = re.findall('\d+', elapsed_time) if len(d_h_m_s) == 4 : seconds = sum([a*b for a,b in zip(multipliers4, map(int,d_h_m_s))]) elif len(d_h_m_s) == 3 : seconds = sum([a*b for a,b in zip(multipliers3, map(int,d_h_m_s))]) return seconds def check_pending_projects ( project_id = None, email = None) : """ Gets the list of pending project for approval in GOLD table 'g_lp_applications' """ pending_projects = [] connection = application_db_engine.connect() if project_id is None : if email : ### Managers see only their own pending applications result = connection.execute("select\ id,\ requestor,\ email,\ institution,\ country,\ project_name,\ cpu_hours,\ description,\ applications,\ start_date,\ end_date,\ date_of_application\ from\ g_lp_applications\ where\ actual_status = 'pending' and email = '%s' " % email) else : ### Administrator see all pending applications result = connection.execute("select\ id,\ requestor,\ email,\ institution,\ country,\ project_name,\ cpu_hours,\ description,\ applications,\ start_date,\ end_date,\ date_of_application\ from\ g_lp_applications\ where\ actual_status = 'pending'") else : ### Project displayed for modifications before final approval result = connection.execute("select\ id,\ requestor,\ email,\ institution,\ country,\ project_name,\ cpu_hours,\ description,\ applications,\ start_date,\ end_date,\ date_of_application\ from\ g_lp_applications\ where\ id = '%s' " % project_id) for row in result: pending_projects.append(row) connection.close() return pending_projects def approve_pending_project ( kwd) : """ Approves (activates) a pending project from GOLD table 'g_lp_applications' Data collected so has to be put into as follows : --- both for GOLD DB and Lifeportal application table : email, cpu_amount, start_date, end_date --- for GOLD original DB : description --- for Lifeproject application table : project_id Still needed to be generated by this function : --- both for GOLD DB and Lifeportal application table : project_code (lpXX) --- for Lifeproject application table : actual_status, last_modified, status_before_last_modification, last_modified_by """ print "All kwd approve pending project ", kwd connection = application_db_engine.connect() last_modified_by = kwd['last_modified_by'].strip() project_id = kwd['project_id'] email = kwd['email'].strip() cpu_amount = kwd['cpu_hours'] description = kwd['description'] start_date = kwd['start_date'] end_date = kwd['end_date'] status_before_last_modification = 'pending' project_name = kwd['project_name'] application_date = kwd['application_date'] ## ################### Reject Lifeportal Application ################# if 'reject_pending_project' in kwd : if 'reason_for_rejection' in kwd : reason_for_rejection_email_version = kwd['reason_for_rejection'] reason_for_rejection = re.escape(kwd['reason_for_rejection']) del kwd['reason_for_rejection'] else : reason_for_rejection = "NA" connection.execute("update g_lp_applications set \ reason_for_rejection = '%s', \ last_modified_by = '%s', \ last_modified = NOW(), \ actual_status = 'rejected' \ where \ id = '%s' " % ( reason_for_rejection, last_modified_by, project_id ) ) ## Flush button del kwd['reject_pending_project'] ## Send application rejection email sender = 'lifeportal-help@usit.uio.no' receiver = email bcc = 'lifeportal-lrfk@usit.uio.no' project_string = '\nproject name : ' + project_name + '\ncpu_amount : ' + cpu_amount + '\ndescription : ' + description + '\nstart_date : ' + start_date + '\nend_date : ' + end_date + '\napplication_date : ' + application_date header = 'To:' + receiver + '\nFrom: ' + sender + '\nBcc: ' + bcc + '\nSubject:Your Lifeportal application has been rejected \n' email_msg = header + '\nYour Lifeportal application :\n' + project_string + '\n\nhas been rejected for the following reasons: ' + reason_for_rejection_email_version + '\nPlease, apply again.\n\nThe Lifeportal Resource Allocation Committee' ##For debugging msg = email_msg msg = msg.encode('utf-8') print "=== EMAIL application rejection notification message ===\n", msg try: smtpObj = smtplib.SMTP('localhost') smtpObj.sendmail(sender, [receiver,bcc], email_msg.encode('utf-8')) print "Successfully sent rejection email" except SMTPException: print "Error: unable to send rejection email" ## ################### Approve Lifeportal Application ################# elif 'approve_pending_project' in kwd : message = '' ## Generate the project code (lpXX) project_code = _generate_project_name() ## Create project in GOLD DB - it creates Organization, project, account and deposits the cpu_amount into the account message = add_project_to_GOLD( email, project_code, cpu_amount, project_name, start_date, end_date) print "Approved application is a project in GOLD DB - READY! ", message ## Add the project manager to the project users list ## the function takes a dictionary, so we need to send email as a 1 element dictionary emails = [] emails.append(email) (associate_user_message,status) = associate_users_to_projects ( emails, project_code ) message = message + '\n' + associate_user_message ## Update Lifeproject application table connection.execute("update g_lp_applications set \ status_before_last_modification = 'pending',\ last_modified = NOW(),\ last_modified_by = '%s' ,\ actual_status = 'approved',\ project_code = '%s' ,\ cpu_hours = '%s',\ start_date = '%s',\ end_date = '%s' \ where \ id = '%s' " % ( last_modified_by, project_code, cpu_amount, start_date, end_date, project_id ) ) ## Flush button del kwd['approve_pending_project'] ## Add project owner to project manager list (project_manager.txt) if not already registered project_manager_message = Project_managers.add_project_manager (email ) if not re.match(r'Error', project_manager_message) : message = message + '\n' + project_manager_message print "Project manager fixed!" ## Send email notification about the approved project sender = 'lifeportal-help@usit.uio.no' receiver = email bcc = 'lifeportal-lrfk@usit.uio.no' header = 'To:' + receiver + '\nFrom: ' + sender + '\nBcc: ' + bcc +'\nSubject:Your Lifeportal application has been approved \n' email_msg = header + '\nYour Lifeportal application :\n' + project_name + '\n\nhas been approved today.\nBest regards,\n\nThe Lifeportal Resource Allocation Committee' ##For debugging msg = email_msg msg = msg.encode('utf-8') print "=== EMAIL application approval notification message ===\n", msg try: smtpObj = smtplib.SMTP('localhost') smtpObj.sendmail(sender, [receiver,bcc], email_msg.encode('utf-8')) print "Successfully sent email" except SMTPException: print "Error: unable to send email" return message def check_rejected_projects ( project_id = None, email = None) : """ Gets the list of pending projects for approval in GOLD table 'g_lp_applications' """ rejected_projects = [] connection = application_db_engine.connect() if project_id is None : if email : ### Managers see only their own pending applications result = connection.execute("select\ id,\ requestor,\ email,\ institution,\ country,\ project_name,\ cpu_hours,\ description,\ applications,\ start_date,\ end_date,\ date_of_application,\ reason_for_rejection\ from\ g_lp_applications\ where\ actual_status = 'rejected' and email = '%s' " % email) else : ### Administrators see all pending applications result = connection.execute("select\ id,\ requestor,\ email,\ institution,\ country,\ project_name,\ cpu_hours,\ description,\ applications,\ start_date,\ end_date,\ date_of_application, \ reason_for_rejection\ from\ g_lp_applications\ where\ actual_status = 'rejected'") else : ### Project displayed for modifications before final approval result = connection.execute("select\ id,\ requestor,\ email,\ institution,\ country,\ project_name,\ cpu_hours,\ description,\ applications,\ start_date,\ end_date,\ date_of_application,\ reason_for_rejection\ from\ g_lp_applications\ where\ id = '%s' " % project_id) for row in result: rejected_projects.append(row) connection.close() return rejected_projects def register_project_application ( kwd) : """ Registers a Lifeportal application ans sets it into the GOLD table 'g_lp_applications' """ print "All kwd register application ", kwd connection = application_db_engine.connect() if 'agree_checkbox' in kwd and kwd['agree_checkbox'] == 'on' and 'tsd_checkbox' in kwd and kwd['tsd_checkbox'] == 'on': ## block if missing name if kwd['name'] : name = re.escape(kwd['name']) else : message = "Please add the name of the responsible person!" status = 'error' return (message,status) ## block if missing job_title if kwd['job_title'] : job_title = re.escape(kwd['job_title']) else : message = "Please add job title!" status = 'error' return (message,status) email = kwd['email'] ## block if missing cellphone if kwd['cellphone'] : cellphone = re.escape(kwd['cellphone']) else : message = "Please add cellphone number!" status = 'error' return (message,status) ## phone phone = '' if not kwd['phone'] : phone = kwd['cellphone'] else : phone = kwd['phone'] ## block if missing institution if kwd['institution'] : institution = re.escape(kwd['institution']) else : message = "Please add institution!" status = 'error' return (message,status) ## block if missing country if kwd['country'] : country = re.escape(kwd['country']) else : message = "Please add country!" status = 'error' return (message,status) ## block if missing project_name if kwd['project_name'] : project_name = re.escape(kwd['project_name']) else : message = "Please add project name!" status = 'error' return (message,status) ## block if missing cpu_hours if kwd['cpu_hours'] and kwd['cpu_hours'].isdigit() : cpu_amount = re.escape(kwd['cpu_hours']) else : message = "Please add cpu hours or check your format! The field may only contain digits!" status = 'error' return (message,status) ## block if no applications are selected applications = '' if 'applications' in kwd : applications_list = kwd['applications'] if isinstance(applications_list,list) : applications = ",".join(applications_list) elif isinstance(applications_list,unicode) : applications = applications_list else : message = "Please select Applications from the Application dropdown, click on it to display application list !" status = 'error' return (message,status) ## block if missing description if kwd['project_description'] : description_email_version = kwd['project_description'] description = re.escape(kwd['project_description']) else : message = "Please add project description!" status = 'error' return (message,status) start_date = kwd['start_date'] end_date = kwd['end_date'] last_modified_by = kwd['last_modified_by'] ## Update Lifeproject application table connection.execute("insert into g_lp_applications (\ requestor ,\ requestor_job, \ email, \ phone ,\ cellphone, \ institution, \ country, \ project_name, \ cpu_hours, \ applications,\ description, \ start_date, \ end_date ,\ date_of_application, \ actual_status, \ last_modified, \ last_modified_by, \ status_before_last_modification ) \ VALUES (\ '%s' ,\ '%s', \ '%s', \ '%s', \ '%s' ,\ '%s', \ '%s', \ '%s', \ '%d' ,\ '%s', \ '%s', \ '%s', \ '%s', \ NOW(), \ 'pending',\ NOW(), \ '%s', \ 'pending' ) " % ( name, job_title, email, phone, cellphone, institution, country, project_name, int(cpu_amount), applications, description, start_date, end_date, last_modified_by ) ) message = "Application stored as 'pending'. A committee will review it as soon as possible and come back to you with further information." status = 'done' ## Send confirmation sender = 'lifeportal-lrfk@usit.uio.no' replyto = 'lifeportal-help@usit.uio.no' bcc = 'lifeportal-lrfk@usit.uio.no' receiver = email project_string = '\nproject name : ' + project_name + '\ncpu_amount : ' + cpu_amount + '\ndescription : ' + description_email_version + '\nstart_date : ' + start_date + '\nend_date : ' + end_date + '\nInstitution :' + institution header = 'To:' + receiver + '\nFrom:' + sender + '\nReply-to:' + replyto + '\nBcc:' + bcc + '\nSubject:Your Lifeportal application has been registered \n' email_msg = header + '\nYour Lifeportal application :\n' + project_string + '\n\nhas been registered.\n\nThe Lifeportal Resource Allocation Committee ' ##For debugging msg = email_msg msg = msg.encode('utf-8') print "=== EMAIL application received message ===\n", msg try: smtpObj = smtplib.SMTP('localhost') smtpObj.sendmail(sender, [receiver,bcc], email_msg.encode('utf-8')) print "Successfully sent application received email" except SMTPException: print "Error: unable to send application received email" return (message, status) else : message = "Please select the agreement checkbox and the non-sensitive data checkbox below !" status = 'error' return (message,status) def job_check_project_balance (project, username, requested_time) : """ Check jobs balance prior to reservation / check multiple reservations """ if re.search("gx_default", project ): balance_command = "sudo /opt/gold/bin/gbalance --show Available -p gx_default -u %s" % ( username ) else : balance_command = "sudo /opt/gold/bin/gbalance --show Available -p %s " % (project) p = subprocess.Popen(balance_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() available = 0 for line in p.stdout.readlines(): if re.search("\d+",line) : available = int(line.strip()) if available < int(requested_time) : return ("low_balance", available) else : ## block multiple reservations for the same amount of cpu hours (balance) (status_reservation, reservation) = job_reservations_check (int(requested_time), int(available), project, username) #print "Accounting.py TOTAL AVAILABLE CPU HOURS", available #print "Accounting.py REQUESTED CPU HOURS ", requested_time #print "Accounting.py STATUS RESERVATION ", status_reservation, " RESERVED : ", reservation if re.search("reservation_over_balance_limit",status_reservation) : return ("reservation_over_balance_limit", reservation) else : return ("ok", reservation ) def job_reservations_check (requested_time, available, project, username) : """ Reserve time used to place a hold on the user account before a job starts to ensure that the credits will be there when it completes. e.g. greserve -J lp7 -p -u -m Abel-Galaxy -P 2 -t 3600 """ reserve_command = "sudo /opt/gold/bin/glsres --show Amount -p %s -u %s" % (project,username) p = subprocess.Popen(reserve_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() ## get the existing reservations amount reserved = 0 for line in p.stdout.readlines(): if re.search("^\d+$",line) : reserved = int(line) if isinstance( reserved, int) : reserved += reserved print "Pending reservations ", reserved remaining = available - reserved print "Remaining hours ", remaining if requested_time > remaining: over_limit = requested_time-remaining return ("reservation_over_balance_limit", over_limit) else : return ("ok", requested_time) def job_reserve(jobname,project, username, time, pe) : """ Reserve time used to place a hold on the user account before a job starts to ensure that the credits will be there when it completes. e.g. greserve -J lp7 -p -u -m Abel-Galaxy -P 2 -t 3600 """ machine = os.environ['LAPINSTANCE'] version = os.environ['LAPVERSION'] reserve_command = "sudo /opt/gold/bin/greserve -J '%s' -p '%s' -u %s -m '%s (%s)' -P '%s' -t '%s'" % (jobname,project,username,machine,version,pe,time) p = subprocess.Popen(reserve_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): if re.search("Successfully ",line) : return line else : print line def job_charge( slurm_job_id, galaxy_job_id ): """ Deprecated : Called from ../runners/drmaa.py (finction 'finish_job'). It charges the project by the elapsed time Called from ../runners/drmaa.py (finction '_complete_terminal_job'). It charges the project by the elapsed time """ command = "sacct -j "+slurm_job_id+" -p -o jobid,jobname,alloccpus,elapsed,nodelist,account -X" p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() job_data ={} job_info = [] for line in p.stdout.readlines(): print "SACCT LINE ", line job_info=line.split('|') ## Check if the project is a portal project and only then charge! :: concatenates the username if re.search('::',job_info[1]) : if job_info[0] == slurm_job_id : job_data['slurm_job_id'] = job_info[0] ##Split username and lifeportal name if relevant (user,portal_project_name) = job_info[1].split('::') job_data['user'] = user job_data['portal_project'] = portal_project_name job_data['processes'] = job_info[2] ## process slurm elapsed time slurm_time_secs = "source /home/laportal/additional_tools/slurm_utils.sh ; slurm_time_secs %s " % job_info[3] p = subprocess.Popen(slurm_time_secs, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): print "LINE ",line if re.match('^\d+$',line) : job_data['charge_duration'] = line.strip() job_data['machine'] = job_info[4] job_data['galaxy_job_id'] = galaxy_job_id job_data['slurm_account'] = job_info[5] pprint(job_data) # charge the job machine = os.environ['LAPINSTANCE'] version = os.environ['LAPVERSION'] job_charge_command = "sudo /opt/gold/bin/gcharge -J '%s' -p '%s' -u '%s' -m '%s (%s)' -P '%s' -t '%s'" % ( job_data['galaxy_job_id'], job_data['portal_project'], job_data['user'], machine, version, job_data['processes'], job_data['charge_duration']) p = subprocess.Popen(job_charge_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for line in p.stdout.readlines(): print line return line else : return "Not a portal job, no charges registered!" def collect_project_info_for_report ( project_code ) : """ Gets the information about the project for a report """ connection = application_db_engine.connect() if project_code : ### Project displayed for modifications before final approval result = connection.execute("select\ requestor,\ requestor_job, \ email,\ institution,\ country,\ project_name,\ cpu_hours,\ start_date,\ end_date \ from\ g_lp_applications\ where\ project_code = '%s' " % project_code) project_data = None if not result : print "No project data to generate the report!" return project_data for row in result: project_data = list(row) # convert pu hours into hours cpu_hours = project_data[6] project_data[6] = float(cpu_hours) get_account_info_command = "sudo /opt/gold/bin/gbalance -h --show Name,Available | grep -w %s | uniq " % project_code p = subprocess.Popen(get_account_info_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() available = () for line in p.stdout.readlines(): account_info = line.split() available = account_info[1] available = float(available) project_data.insert(9,project_data[6]-available) project_data.insert(10,available) project_data.insert(11,project_code) connection.close() print "Accounting.py PROJECT data ", project_data return project_data