boinc/py/Boinc/add_util.py

180 lines
6.1 KiB
Python

#!/usr/bin/env python
# $Id$
# add_util.py - code shared between add and xadd
import database, tools
import time, pprint
import MySQLdb
CREATE_TIME = ['?create_time', int(time.time())]
TRANSITION_TIME = ['?transition_time', int(time.time())]
class XAppVersion(database.AppVersion):
def __init__(self,**kwargs):
kwargs['xml_doc'] = tools.process_app_version(
app = kwargs['app'],
version_num = int(kwargs['version_num']),
exec_files = kwargs['exec_files'],
signature_files = kwargs.setdefault('signature_files',{}))
del kwargs['signature_files']
del kwargs['exec_files']
del kwargs['exec_file']
database.AppVersion.__init__(self,**kwargs)
# format: [ database.Object, args, ...]
# arg format:
# 'arg'
# '?arg' optional
# [ 'arg', default_value ]
# NOTE TO KARL: If cross_project_id is not supplied, the default value
# should be a random 32 character script obtained by doing the md5sum
# hash of (say) 512 bytes from /dev/urandom or similar.
list_objects_to_add = [
[ database.Platform, 'name', 'user_friendly_name', CREATE_TIME ],
[ database.App, 'name', 'user_friendly_name', ['?min_version',0], CREATE_TIME],
[ XAppVersion, 'app', 'platform', 'version_num', 'exec_file', '?signature_file',
CREATE_TIME ],
[ database.User, 'name', 'email_addr', 'authenticator',
['?country','United States'], ['?postal_code','0'], ['?cross_project_id', '0'],
'?global_prefs', '?global_prefs_file',
CREATE_TIME ],
# [ database.Workunit, 'zzzz' ],
]
class AddObject:
pass
add_objects = {}
for o in list_objects_to_add:
add_object = AddObject()
add_object.DatabaseObject = o[0]
add_object.name = add_object.DatabaseObject._table.table
add_object.args = []
add_object.optional_args = []
add_object.default_values = {}
for arg in o[1:]:
if isinstance(arg, list):
default_value = arg[1]
arg = arg[0]
else:
default_value = None
if arg.startswith('?'):
optional = True
arg = arg[1:]
else:
optional = False
if optional:
add_object.optional_args.append(arg)
else:
add_object.args.append(arg)
if default_value:
add_object.default_values[arg] = default_value
add_objects[add_object.name] = add_object
most_recent_exec_file = None
def translate_arg(object, arg, value, args_dict):
'''Translate various user argument values, for adding given ``object``.
Modifies ``args_dict``.'''
database_table = None
try:
database_table = database.__dict__[arg.capitalize()]._table
except:
pass
if database_table:
args_dict[arg] = translate_database_arg(database_table, arg, value)
return
if arg == 'global_prefs_file':
args_dict['global_prefs'] = open(value).read()
return
if object.DatabaseObject == XAppVersion:
# 'add app_version' accepts multiple '-exec_file's with
# '-signature_file' applying to the most recent exec_file
if arg == 'exec_file':
global most_recent_exec_file
most_recent_exec_file = value
args_dict.setdefault('exec_files',[]).append(value)
# since 'exec_file' (without 's') is required, set it to None so
# that argument checker knows we got one; we'll delete it later.
args_dict[arg] = None
return
if arg == 'signature_file':
args_dict.setdefault('signature_files',{})[most_recent_exec_file] = value
return
if arg == 'cross_project_id':
if not value:
value = tools.make_uuid()
args_dict[arg] = value
def translate_database_arg(database_table, arg, value):
'''Look up an object ``value`` either as a database ID or string.
This allows us to accept e.g. either --app Astropulse or --app 1'''
try:
id = int(value)
results = database_table.find(id=id)
if not results:
raise Exception("")
except:
results = database_table.find(name=value)
if len(results) == 0:
raise SystemExit('No %s "%s" found' %(arg,value))
if len(results) > 1:
print >>sys.stderr, 'Too many %ss match "%s": '%(arg,value)
for result in results:
print >>sys.stderr, ' ', result.name
raise SystemExit
return results[0]
class AddObjectException(Exception): pass
def check_required_arguments(add_object, args_dict):
for arg in add_object.args:
if not arg in args_dict:
raise AddObjectException('required value for %s not given'%arg)
def translate_args_dict(add_object, untranslated_args_dict):
args_dict = add_object.default_values.copy()
for arg,value in untranslated_args_dict.items():
translate_arg(add_object,arg,value,args_dict)
return args_dict
def exception_is_duplicate_entry(exception):
'''Checks a MySQLdb.IntegrityError for whether the error is Duplicate
Entry. Kludgy.'''
return (isinstance(exception, MySQLdb.IntegrityError) and
str(exception).find('Duplicate entry')!=-1)
def do_add_object(add_object, untranslated_args_dict, skip_old=False):
'''Input ```args_dict``` must have been translated already.'''
args_dict = translate_args_dict(add_object, untranslated_args_dict)
check_required_arguments(add_object, args_dict)
dbobject = add_object.DatabaseObject(**args_dict)
print "Processing", dbobject, "..."
# print "Commiting", dbobject, "with args:"
# pprint.pprint(dbobject.__dict__)
try:
dbobject.commit()
except MySQLdb.MySQLError, e:
if skip_old and exception_is_duplicate_entry(e):
print " Skipped existing", dbobject
return
else:
raise SystemExit('Error committing %s: %s' %(dbobject, e))
# delete object and re-select it from database to allow user to check
# consistency
id = dbobject.id
del dbobject
dbobject = add_object.DatabaseObject._table[id]
print " Committed", dbobject, "; values:"
pprint.pprint(dbobject.__dict__)