xadd stuff

svn path=/trunk/boinc/; revision=2867
This commit is contained in:
Karl Chen 2004-01-13 06:51:19 +00:00
parent 73e397f703
commit cd8e9396a7
10 changed files with 481 additions and 298 deletions

View File

@ -9091,3 +9091,39 @@ David 9 Jan 2004
win_build/
boinc.sln
*.vcproj
Karl 2004-01-12
- BOINC how has a project.xml file (by default in the same location as
config.xml) that can contain database information:
- projects
- platforms
- core versions
- apps
- app versions
- this information used to be added one at a time using the `add'
command-line tool; now there is a new tool `xadd' available, which
parses project.xml and adds anything necessary.
- refactored configxml.py into boincxml.py and configxml.py:
- boincxml.py contains generic XML utility code
- configxml.py contains code specific to config.xml and run_state.XML
- external interface to configxml.py unchanged
- new projectxml.py that parses and writes project.xml
- refactored tools/add into tools/add and py/Boinc/add_util.py
- added new addable fields
py/Boinc/
boinc_project_path.py
configxml.py
boincxml.py (new)
projectxml.py (new)
add_util.py (new)
db_base.py
tools/
xadd (new)
add

179
py/Boinc/add_util.py Normal file
View File

@ -0,0 +1,179 @@
#!/usr/bin/env python
# $Id$
# add_util.py - code shared between add and xadd
from Boinc import database, tools
import time, pprint
import MySQLdb
CREATE_TIME = ['?create_time', int(time.time())]
TRANSITION_TIME = ['?transition_time', int(time.time())]
class XCoreVersion(database.CoreVersion):
def __init__(self,**kwargs):
kwargs['xml_doc'] = tools.process_executable_file(kwargs['exec_file'])
del kwargs['exec_file']
database.CoreVersion.__init__(self, **kwargs)
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 ]
list_objects_to_add = [
[ database.Project, 'short_name', 'long_name' ],
[ database.Platform, 'name', 'user_friendly_name', CREATE_TIME ],
[ XCoreVersion, 'platform', 'version_num', 'exec_file',
['?message',''], ['?message_priority','']],
[ 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','94703'],
'?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
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__)

View File

@ -3,7 +3,7 @@
# boinc_project_path.py - defines the directories where config.xml and
# run_state.xml are kept.
#
# You can override these defaults
# You can override these defaults with one of these options:
# 1) modify this file directly (if you have only one project on your server
# or have separate copies for each)
# 2) create a new boinc_project_path.py and place it earlier in PYTHONPATH
@ -19,6 +19,10 @@ config_xml_filename = os.environ.get(
'BOINC_CONFIG_XML',
os.path.join(PROGRAM_PARENT_DIR,'config.xml'))
project_xml_filename = os.environ.get(
'BOINC_PROJECT_XML',
os.path.join(PROGRAM_PARENT_DIR,'project.xml'))
run_state_xml_filename = os.environ.get(
'BOINC_RUN_STATE_XML',
os.path.join(PROGRAM_PARENT_DIR,'run_state.xml'))

139
py/Boinc/boincxml.py Normal file
View File

@ -0,0 +1,139 @@
#!/usr/bin/env python
# $Id$
# boincxml.py - xml utilities for boinc
import sys
import xml.dom.minidom
def append_new_element(parent_node, name):
new_element = xml.dom.minidom.Element(name)
if isinstance(parent_node,xml.dom.minidom.Document):
new_element.ownerDocument = parent_node
else:
assert(parent_node.ownerDocument)
new_element.ownerDocument = parent_node.ownerDocument
parent_node.appendChild(new_element)
return new_element
def get_elements(node, name):
return node.getElementsByTagName(name)
def get_element(node, name, optional=True):
try:
return get_elements(node,name)[0]
except IndexError:
if optional:
return append_new_element(node, name)
raise SystemExit("ERROR: Couldn't find xml node <%s>"% name)
def _None2Str(object):
if object == None:
return ''
else:
return object
def get_element_data(node):
return node and _None2Str(node.firstChild and node.firstChild.data)
def get_element_int(node, default=0):
try:
return int(_get_element_data(node))
except:
return default
def get_child_elements(node):
return filter(lambda node: node.nodeType == node.ELEMENT_NODE, node.childNodes)
def set_element(node, new_data):
if node.firstChild and node.firstChild.data:
node.firstChild.data = str(new_data)
else:
new_data_node = xml.dom.minidom.Text()
new_data_node.data = str(new_data)
new_data_node.ownerDocument = node.ownerDocument
node.appendChild(new_data_node)
def strip_white_space(node):
''' strip white space from text nodes, and remove empty nodes. '''
# the [:] below is to copy the list since removing children modifies the
# list.
for child in node.childNodes[:]:
if child.nodeType == child.TEXT_NODE:
child.data = child.data.strip()
if not child.data:# and (child.nextSibling or child.previousSibling):
node.removeChild(child)
else:
strip_white_space(child)
def get_elements_as_dict(node):
dict = {}
for child in get_child_elements(node):
# note: str() changes from unicode to single-byte strings
dict[str(child.nodeName)] = get_element_data(child)
return dict
class ConfigDict:
def __init__(self, dom_node):
self._node = dom_node
self._name = self._node.nodeName
self.__dict__.update(get_elements_as_dict(self._node))
def save(self):
for key in self.__dict__:
if key.startswith('_'): continue
set_element( get_element(self._node,key,1), str(self.__dict__[key]) )
def debug_print(self):
for key in self.__dict__.keys():
print key.rjust(15), '=', self.__dict__[key]
class ConfigDictList(list):
def __init__(self, dom_node, item_class=ConfigDict):
self._node = dom_node
list.__init__(self, map(item_class, get_child_elements(self._node)))
def save(self):
map(ConfigDict.save, self)
def make_node_and_append(self, name):
'''Make a new ConfigDict and append it. Returns new ConfigDict.'''
new_element = append_new_element(self._node, name)
new_cd = ConfigDict(new_element)
self.append(new_cd)
return new_cd
class XMLConfig:
'''Base class for xml config files'''
def __init__(self, filename = None):
# default_filename should be defined by children
self.filename = filename or self.default_filename
def _init_empty_xml(self):
self.xml = xml.dom.minidom.parseString(self.default_xml)
def init_empty(self):
self._init_empty_xml()
self._get_elements()
return self
def read(self, failopen_ok=False):
"""Read the XML object's file source and return self."""
try:
self.xml = xml.dom.minidom.parse(self.filename)
strip_white_space(self.xml)
except IOError, e:
if not failopen_ok:
# raise
raise Exception("%s: Couldn't parse XML config\n%s: %s"%(sys.argv[0],sys.argv[0],e))
print >>sys.stderr, "Warning:", e
# self.xml = xml.dom.minidom.Document()
self._init_empty_xml()
self._get_elements()
return self
def _get_elements(self):
pass
def write(self, output=None):
"""Write XML data to filename, or other stream."""
self._set_elements()
if not output:
output = open(self.filename,'w')
self.xml.writexml(output, "", " ", "\n")
print >>output
return self
def _set_elements(self):
pass

View File

@ -22,136 +22,11 @@ USAGE: from Boinc import configxml
'''
import sys
import xml.dom.minidom
import boinc_project_path
from boincxml import *
default_config_file = None
def _append_new_element(parent_node, name):
new_element = xml.dom.minidom.Element(name)
if isinstance(parent_node,xml.dom.minidom.Document):
new_element.ownerDocument = parent_node
else:
assert(parent_node.ownerDocument)
new_element.ownerDocument = parent_node.ownerDocument
parent_node.appendChild(new_element)
return new_element
def _get_elements(node, name):
return node.getElementsByTagName(name)
def _get_element(node, name, optional=True):
try:
return _get_elements(node,name)[0]
except IndexError:
if optional:
return _append_new_element(node, name)
raise SystemExit("ERROR: Couldn't find xml node <%s>"% name)
def _None2Str(object):
if object == None:
return ''
else:
return object
def _get_element_data(node):
return node and _None2Str(node.firstChild and node.firstChild.data)
def _get_element_int(node, default=0):
try:
return int(_get_element_data(node))
except:
return default
def _get_child_elements(node):
return filter(lambda node: node.nodeType == node.ELEMENT_NODE, node.childNodes)
def _set_element(node, new_data):
if node.firstChild and node.firstChild.data:
node.firstChild.data = str(new_data)
else:
new_data_node = xml.dom.minidom.Text()
new_data_node.data = str(new_data)
new_data_node.ownerDocument = node.ownerDocument
node.appendChild(new_data_node)
def strip_white_space(node):
''' strip white space from text nodes, and remove empty nodes. '''
# the [:] below is to copy the list since removing children modifies the
# list.
for child in node.childNodes[:]:
if child.nodeType == child.TEXT_NODE:
child.data = child.data.strip()
if not child.data:# and (child.nextSibling or child.previousSibling):
node.removeChild(child)
else:
strip_white_space(child)
class ConfigDict:
def __init__(self, dom_node):
self._node = dom_node
self._name = self._node.nodeName
for node in _get_child_elements(self._node):
self.__dict__[node.nodeName] = _get_element_data(node)
def save(self):
for key in self.__dict__:
if key.startswith('_'): continue
_set_element( _get_element(self._node,key,1), str(self.__dict__[key]) )
def debug_print(self):
for key in self.__dict__.keys():
print key.rjust(15), '=', self.__dict__[key]
class ConfigDictList(list):
def __init__(self, dom_node, item_class=ConfigDict):
self._node = dom_node
list.__init__(self, map(item_class, _get_child_elements(self._node)))
def save(self):
map(ConfigDict.save, self)
def make_node_and_append(self, name):
'''Make a new ConfigDict and append it. Returns new ConfigDict.'''
new_element = _append_new_element(self._node, name)
new_cd = ConfigDict(new_element)
self.append(new_cd)
return new_cd
class XMLConfig:
'''Base class for xml config files'''
def __init__(self, filename = None):
# default_filename should be defined by children
self.filename = filename or self.default_filename
def _init_empty_xml(self):
self.xml = xml.dom.minidom.parseString(self.default_xml)
def init_empty(self):
self._init_empty_xml()
self._get_elements()
return self
def read(self, failopen_ok=False):
"""Read the XML object's file source and return self."""
try:
self.xml = xml.dom.minidom.parse(self.filename)
strip_white_space(self.xml)
except IOError, e:
if not failopen_ok:
# raise
raise Exception("%s: Couldn't parse XML config\n%s: %s"%(sys.argv[0],sys.argv[0],e))
print >>sys.stderr, "Warning:", e
# self.xml = xml.dom.minidom.Document()
self._init_empty_xml()
self._get_elements()
return self
def _get_elements(self):
pass
def write(self, output=None):
"""Write XML data to filename, or other stream."""
self._set_elements()
if not output:
output = open(self.filename,'w')
self.xml.writexml(output, "", " ", "\n")
print >>output
return self
def _set_elements(self):
pass
class ConfigFile(XMLConfig):
'''
embodies config.xml
@ -162,14 +37,14 @@ class ConfigFile(XMLConfig):
'''
default_filename = boinc_project_path.config_xml_filename
def __init__(self, *args, **kwargs):
apply(XMLConfig.__init__,(self,)+args,kwargs)
XMLConfig.__init__(self, *args, **kwargs)
global default_config_file
default_config_file = self
def _get_elements(self):
self.xml_boinc = _get_element(self.xml, 'boinc', optional=False)
self.xml_config = _get_element(self.xml_boinc, 'config', optional=False)
self.xml_tasks = _get_element(self.xml_boinc, 'tasks')
self.xml_daemons = _get_element(self.xml_boinc, 'daemons')
self.xml_boinc = get_element(self.xml, 'boinc', optional=False)
self.xml_config = get_element(self.xml_boinc, 'config', optional=False)
self.xml_tasks = get_element(self.xml_boinc, 'tasks')
self.xml_daemons = get_element(self.xml_boinc, 'daemons')
self.config = ConfigDict(self.xml_config)
self.daemons = ConfigDictList(self.xml_daemons)
self.tasks = ConfigDictList(self.xml_tasks)
@ -208,18 +83,19 @@ class RunStateFile(XMLConfig):
'''
default_filename = boinc_project_path.run_state_xml_filename
def _get_elements(self):
self.xml_boinc = _get_element(self.xml, 'boinc', optional=False)
self.xml_tasks = _get_element(self.xml_boinc, 'tasks')
self.xml_enabled = _get_element(self.xml_boinc, 'enabled')
self.xml_boinc = get_element(self.xml, 'boinc', optional=False)
self.xml_tasks = get_element(self.xml_boinc, 'tasks')
self.xml_enabled = get_element(self.xml_boinc, 'enabled')
self.tasks = ConfigDictList(self.xml_tasks)
self.enabled = _get_element_int(self.xml_enabled)
self.enabled = get_element_int(self.xml_enabled)
def _set_elements(self):
_set_element( self.xml_enabled, self.enabled )
set_element( self.xml_enabled, self.enabled )
self.tasks.save()
default_xml = '<boinc></boinc>'
def default_config():
'''If any config file has been read, return it. Else open the default one and return it.'''
'''If any config file has been read, return it. Else open the default one
and return it.'''
if not default_config_file: ConfigFile().read()
assert(default_config_file)
return default_config_file

View File

@ -57,6 +57,14 @@ class Debug:
debug = Debug()
debug.mysql = not not os.environ.get('DEBUG_DB')
def _execute_sql(cursor, command):
'''Same as ``cursor.execute(command)``, but more verbose on error.'''
try:
cursor.execute(command)
except MySQLdb.MySQLError, e:
e.args += (command,)
raise e
def _commit_object(tablename, paramdict, id=None):
"""Takes a tablename, a parameter dict, and an optional id. Puts together
the appropriate SQL command to commit the object to the database.
@ -77,14 +85,14 @@ def _commit_object(tablename, paramdict, id=None):
(tablename, ', '.join(equalcommands))
if debug.mysql:
debug.printline("query: "+command)
cursor.execute(command)
_execute_sql(cursor, command)
id = cursor.insert_id() #porters note: works w/MySQLdb only
else:
command = "UPDATE %s SET %s WHERE id=%d" % \
(tablename, ', '.join(equalcommands), id)
if debug.mysql:
debug.printline("query: "+command)
cursor.execute(command)
_execute_sql(cursor, command)
cursor.close()
dbconnection.commit()
return id
@ -101,7 +109,7 @@ def _remove_object(command, id=None):
' WHERE id=%d' % id
if debug.mysql:
debug.printline("query: "+command)
cursor.execute(command)
_execute_sql(cursor, command)
cursor.close()
dbconnection.commit()
def _select_object(table, searchdict, extra_args="", extra_params=[], select_what=None):
@ -132,7 +140,7 @@ def _select_object(table, searchdict, extra_args="", extra_params=[], select_wha
cursor = dbconnection.cursor()
if debug.mysql:
debug.printline("query: "+command)
cursor.execute(command)
_execute_sql(cursor, command)
return cursor
def _select_object_fetchall(*args, **kwargs):

58
py/Boinc/projectxml.py Normal file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
# $Id$
# projectxml.py - module to read and parse project.xml
'''
SYNOPSIS: parses and writes project.xml
USAGE: from Boinc import projectxml
project = projectxml.ProjectFile().read()
project.commit_all()
'''
import sys
import boinc_path_config
import boinc_project_path
from Boinc.boincxml import *
from Boinc.add_util import *
default_project_file = None
class ProjectFile(XMLConfig):
'''
embodies project.xml
'''
default_filename = boinc_project_path.project_xml_filename
def __init__(self, *args, **kwargs):
XMLConfig.__init__(self, *args, **kwargs)
global default_project_file
default_project_file = self
def _get_elements(self):
self.xml_boinc = get_element(self.xml, 'boinc', optional=False)
self.add_objects_and_args = []
for node in self.xml_boinc.childNodes:
add_object = add_objects.get(node.nodeName)
if not add_object:
raise SystemExit("Error in %s: No such object '%s' to add." %(self.filename,node.nodeName))
self.add_objects_and_args.append((add_object, get_elements_as_dict(node)))
def _set_elements(self):
assert(0) # TODO (maybe)
def commit_all(self):
'''Commits all new data to the BOINC project database.'''
for add_object, untranslated_args_dict in self.add_objects_and_args:
try:
do_add_object(add_object, untranslated_args_dict, skip_old=True)
except AddObjectException, e:
raise SystemExit('Error in %s: %s' %(self.filename,e))
default_xml = '<boinc></boinc>'
def default_project():
'''If any project file has been read, return it. Else open the default one
and return it.'''
if not default_project_file: ProjectFile().read()
assert(default_project_file)
return default_project_file

178
tools/add
View File

@ -6,7 +6,7 @@
#XXX TODO: add app should modify config.xml to add application-specific daemons
'''
add items to the BOINC database.
add items to the BOINC database -- command-line interface. See also ``xadd``.
Usages:
@ -50,100 +50,10 @@ add workunit (TODO)
add result (TODO) '''
import boinc_path_config
from Boinc import database, db_mid, configxml, tools
from Boinc import database, db_mid, configxml
from Boinc.util import *
import sys, os, getopt, time, pprint
CREATE_TIME = ['?create_time', int(time.time())]
TRANSITION_TIME = ['?transition_time', int(time.time())]
class XCoreVersion(database.CoreVersion):
def __init__(self,**kwargs):
kwargs['xml_doc'] = tools.process_executable_file(kwargs['exec_file'])
del kwargs['exec_file']
apply(database.CoreVersion.__init__,[self],kwargs)
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']
apply(database.AppVersion.__init__,[self],kwargs)
# format: [ database.Object, args, ...]
# arg format:
# 'arg'
# '?arg' optional
# [ 'arg', default_value ]
list_objects_to_add = [
[ database.Project, 'name', '?long_name' ],
[ database.Platform, 'name', 'user_friendly_name', CREATE_TIME ],
[ XCoreVersion, 'platform', 'version_num', 'exec_file',
['?message',''], ['?message_priority','']],
[ database.App, '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','94703'],
'?global_prefs', '?global_prefs_file',
CREATE_TIME ],
# [ database.Workunit, 'zzzz' ],
]
most_recent_exec_file = None
def translate_arg(object, arg, value, args_dict):
'''Translate various arguments'''
database_table = None
try:
database_table = database.__dict__[arg.capitalize()]._table
except:
pass
if database_table:
return (arg,translate_database_arg(database_table, arg, value))
if arg == 'global_prefs_file':
return ('global_prefs', open(value).read())
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 this is required, set it to None so that argument checker
# knows we got one; we'll delete it later.
return (arg,None)
if arg == 'signature_file':
args_dict.setdefault('signature_files',{})[most_recent_exec_file] = value
return (None,None)
return (arg,value)
def translate_database_arg(database_table, arg, value):
'''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]
from Boinc.add_util import *
import sys, os, getopt
def ambiguous_lookup(string, dict):
results = []
@ -156,6 +66,17 @@ def ambiguous_lookup(string, dict):
results.append(dict[key])
return results
def lookup_object_to_add(name_of_object_to_add):
name_of_object_to_add = name_of_object_to_add.strip().lower()
possible_objects = ambiguous_lookup(name_of_object_to_add, add_objects)
if len(possible_objects) == 0:
raise SystemExit("No such object '%s' to add"%name_of_object_to_add)
if len(possible_objects) > 1:
print >>sys.stderr, "Object name '%s' matches multiple objects:"%name_of_object_to_add
for object in possible_objects:
print " ", object.name
raise SystemExit(1)
return possible_objects[0]
def parse_global_options(args):
# raise SystemExit('todo')
@ -179,62 +100,25 @@ def help_object(object, msg=None):
print >>sys.stderr, dv(object,arg)
raise SystemExit
def add_object(object, args):
def commandline_add_object(add_object, args):
try:
parsed_opts, placement_args = \
getopt.getopt(args, '',
map(lambda s: s+'=',
object.args + object.optional_args))
add_object.args + add_object.optional_args))
if placement_args:
raise getopt.GetoptError('Unknown args '+' '.join(placement_args))
except getopt.GetoptError, e:
help_object(object, e)
args_dict = object.default_values.copy()
help_object(add_object, e)
untranslated_args_dict = {}
for arg,value in parsed_opts:
if not arg.startswith('--'):
raise Exception('internal error: arg should start with "--"')
arg = arg[2:]
(arg,value) = translate_arg(object,arg,value,args_dict)
if not arg: continue
args_dict[arg] = value
for arg in object.args:
if not arg in args_dict:
help_object(object, 'required argument --%s not given'%arg)
object = apply(object.DatabaseObject, [], args_dict)
object.commit()
print "Committed", object, "with args:"
pprint.pprint(object.__dict__)
class Dict:
pass
objects_to_add = {}
for o in list_objects_to_add:
object = Dict()
object.DatabaseObject = o[0]
object.name = object.DatabaseObject._table.table
object.args = []
object.optional_args = []
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:
object.optional_args.append(arg)
else:
object.args.append(arg)
if default_value:
object.default_values[arg] = default_value
objects_to_add[object.name] = object
untranslated_args_dict[arg[2:]] = value
try:
do_add_object(add_object, untranslated_args_dict)
except AddObjectException, e:
help_object(add_object, e)
if len(sys.argv) < 2:
print >>sys.stderr, """Syntax: add <object_to_add> <options...>
@ -247,6 +131,7 @@ Objects to add:"""
print >>sys.stderr, """
Global options:
--config=config.xml Path to configuration file.
--skip_old Ignore database objects that already exist
These override config.xml:
--db_name Database name
@ -259,16 +144,7 @@ arguments.
raise SystemExit(1)
name_of_object_to_add = sys.argv[1].strip().lower()
possible_objects = ambiguous_lookup(name_of_object_to_add, objects_to_add)
if len(possible_objects) == 0:
raise SystemExit("No such object '%s' to add"%name_of_object_to_add)
if len(possible_objects) > 1:
print >>sys.stderr, "Object name '%s' matches multiple objects:"%name_of_object_to_add
for object in possible_objects:
print " ", object.name
raise SystemExit(1)
add_object = lookup_object_to_add(sys.argv[1])
args = sys.argv[2:]
parse_global_options(args)
@ -276,4 +152,4 @@ parse_global_options(args)
config = configxml.default_config()
database.connect(config.config)
add_object(possible_objects[0], args)
commandline_add_object(add_object, args)

View File

@ -3,11 +3,6 @@
# $Id$
# Creates a new BOINC project.
# TODO: use database.py (mainly for setup_project.py)
# TODO: don't add app / app version here.
# TODO: create 'apps' subdirectory and set it in config.xml
# TODO: use configxml module
import boinc_path_config
from Boinc.setup_project import *
from Boinc import database, db_mid, configxml, tools

12
tools/xadd Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
# $Id$
import boinc_path_config
from Boinc import database, db_mid, configxml, projectxml
config = configxml.default_config()
database.connect(config.config)
projectxml.ProjectFile().read().commit_all()