mirror of https://github.com/BOINC/boinc.git
parent
73e397f703
commit
cd8e9396a7
|
@ -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
|
||||
|
||||
|
|
|
@ -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__)
|
|
@ -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'))
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
178
tools/add
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
Loading…
Reference in New Issue