diff --git a/checkin_notes b/checkin_notes
index a978f009b8..4b8432de11 100644
--- a/checkin_notes
+++ b/checkin_notes
@@ -7275,3 +7275,15 @@ David 28 Aug 2009
cs_cmdline.cpp
lib/
proxy_info.cpp,h
+
+David 28 Aug 2009
+ - add "appmgr" script for managing apps and versions (from Gabor Gombas)
+
+ doc/manpages/
+ appmgr.xml
+ Makefile.xm
+ py/Boinc/
+ setup_project.py
+ tools/
+ appmgr (new)
+ pymw_setup (removed)
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index 6af7629880..9349d4ccf4 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -13,16 +13,19 @@ if ENABLE_MANAGER
endif
if ENABLE_SERVER
- SERVER_MANS =
+ SERVER_MANS = appmgr.8
endif
man_MANS = $(CLIENT_MANS) $(CLIENTGUI_MANS) $(SERVER_MANS)
-SUFFIXES = .xml .1
+SUFFIXES = .xml .1 .8
.xml.1:
$(DOCBOOK2X_MAN) $<
+.xml.8:
+ $(DOCBOOK2X_MAN) $<
+
all-local: $(man_MANS)
CLEANFILES = $(man_MANS)
diff --git a/doc/manpages/appmgr.xml b/doc/manpages/appmgr.xml
new file mode 100644
index 0000000000..375d154eff
--- /dev/null
+++ b/doc/manpages/appmgr.xml
@@ -0,0 +1,640 @@
+
+appmgr">
+]>
+
+
+
+
+ Gábor
+ Gombás
+ gombasg@sztaki.hu
+
+ August 27, 2009
+
+
+
+ &appmgr;
+ 8
+
+
+
+ &appmgr;
+ Tool to manage applications and platforms
+
+
+
+
+ &appmgr;
+ list
+
+
+
+ &appmgr;
+ add
+ NAME
+ DESC
+
+
+
+
+
+
+
+
+
+
+ &appmgr;
+ delete
+ NAME
+
+
+
+
+
+ &appmgr;
+ update
+ NAME
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &appmgr;
+ update_appver
+ NAME
+
+
+
+
+
+
+
+
+
+
+
+ &appmgr;
+ list_platform
+
+
+
+ &appmgr;
+ add_platform
+ NAME
+ DESC
+
+
+ &appmgr;
+ delete_platform
+ NAME
+
+
+
+ &appmgr;
+ update_platform
+ NAME
+
+
+
+
+
+
+
+ &appmgr;
+ add_standard_platforms
+
+
+ &appmgr;
+
+
+
+
+
+
+
+
+ DESCRIPTION
+
+ &appmgr; is a tool to manage platforms, applications and application
+ versions from the command-line.
+
+
+ Note: application refers to data in the
+ app table; application version
+ refers to data in the app_version table and in the
+ file system.
+
+
+
+
+ COMMANDS
+
+ The generic syntax is:
+
+
+
+ &appmgr;
+ COMMAND
+ command arguments
+
+
+
+ Note that command-specific options must came after the command, never before.
+ Here is a short list of the available commands. The following sections describe
+ the commands in detail.
+
+
+
+ list
+
+
+ List the installed applications and application versions.
+
+
+
+
+ add
+
+
+ Add a new application.
+
+
+
+
+ delete
+
+
+ Delete an application or application version.
+
+
+
+
+ update
+
+
+ Update the properties of an application.
+
+
+
+
+ update_appver
+
+
+ Update the properties of an application version.
+
+
+
+
+ list_platform
+
+
+ List the platforms known to the project.
+
+
+
+
+ add_platform
+
+
+ Add a new platform.
+
+
+
+
+ delete_platform
+
+
+ Delete a platform.
+
+
+
+
+ update_platform
+
+
+ Update the properties of a platform.
+
+
+
+
+ add_standard_platforms
+
+
+ Add all standard platforms to the project.
+
+
+
+
+
+
+ LIST
+
+ List the installed applications and application versions, together with
+ any non-default properties (beta, deprecated, min. core version etc.). If the
+ option is specified, then the
+ list of application versions is omitted and just the application definitions
+ are listed.
+
+
+
+
+ ADD
+
+ Add a new application. NAME is
+ the short name of the application as used in the file system.
+ DESC is the user-friendly name that is
+ displayed to users.
+
+
+ The following options can also be specified:
+
+
+
+
+ ,
+ ,
+
+
+
+
+ Set the homogeneous redundancy class. no means
+ all hosts are equivalent, fine means
+ fine-grained classification of 80 classes,
+ coarse means coarse classification of 4
+ classes.
+
+
+
+
+
+
+
+
+
+ Indicate that this is application is in beta testing. Users can select
+ if they are willing to test applications marked as beta in their
+ preferences; see the wiki section "Beta-test applications" about
+ how to enable this on the web interface.
+
+
+
+
+
+
+
+
+
+ Specify the weight of this application if the feeder is run with
+ the option. NUM
+ is a floating-point value, the default is 1.
+
+
+
+
+
+
+
+
+
+ Set the number of replicas when an otherwise unreplicated work
+ unit is sent to a host that is not trusted. See the wiki section
+ "Adaptive replication" for details.
+
+
+
+
+
+
+
+ DELETE
+
+ Delete applications or application versions. This command deletes both the
+ database records and the files under the download and
+ apps directories, so use with care.
+
+
+ The following options can be specified:
+
+
+
+
+
+
+
+
+ Select only application versions with the version number
+ VER.
+ The version number must be
+ specified as major.minor.
+
+
+
+
+
+
+
+
+
+ Select only application versions for the platform
+ NAME.
+
+
+
+
+
+
+
+
+
+ Select only application versions having the plan class
+ NAME.
+
+
+
+
+
+ If more than one of the options ,
+ and are
+ specified, then only application versions matching all the criteria
+ are deleted. If none of these options are specified, then the
+ application is also deleted. Note that if one or more of
+ these options are specified then the application is not
+ deleted even if there are no more application versions remaining.
+
+
+
+
+ UPDATE
+
+ Update the properties of an application. The following properties can
+ be modified:
+
+
+
+
+ ,
+ ,
+
+
+
+
+ Set the homogeneous redundancy class. no means
+ all hosts are equivalent, fine means
+ fine-grained classification of 80 classes,
+ coarse means coarse classification of 4
+ classes.
+
+
+ Do not change the homogeneous redundancy class while there are jobs
+ in progress!
+
+
+
+
+
+
+
+
+
+ Indicate that this is application is in beta testing. Users can select
+ if they are willing to test applications marked as beta in their
+ preferences; see the wiki about how to enable this on the web interface.
+
+
+
+
+
+
+
+
+
+ Indicate that the application is no more in beta testing.
+
+
+
+
+
+
+
+
+
+ Specify the weight of this application if the feeder is run with
+ the option. NUM
+ is a floating-point value, the default is 1.
+
+
+
+
+
+
+
+
+
+ Set the number of replicas when an otherwise unreplicated work
+ unit is sent to a host that is not trusted. See the wiki article
+ on "Adaptive replication" for details.
+
+
+
+
+
+
+
+
+
+ Update the user-friendly description of the application.
+
+
+
+
+
+
+
+
+
+ Set the min. version of the application that the scheduler accepts.
+ This is checked only if the client uses the anonymous
+ platform.
+ The version number must be
+ specified as major.minor.
+
+
+
+
+
+
+
+
+
+ Indicate that the application is deprecated. The feeder will then
+ ignore this application when looking for new work.
+
+
+
+
+
+
+
+
+
+ Remove the deprecation mark from the application.
+
+
+
+
+
+
+
+ UPDATE_APPVER
+
+ Update the properties of an application version. The version(s) to operate
+ on can be specified with the ,
+ and options, as described at the DELETE
+ command.
+
+
+ The following properties can be modified:
+
+
+
+
+
+
+
+
+ Set the min. version of the core client that is required to run
+ this application version.
+ The version number must be specified as
+ major.minor. The 3rd component (the patch
+ level) of the core client version is ignored.
+
+
+
+
+
+
+
+
+
+ Set the max. version of the core client that is allowed to run
+ this application. Note that the scheduler ignores this setting currently.
+
+
+
+
+
+
+
+
+
+ Indicate that the application version is deprecated. The feeder will then
+ ignore this version when looking for new work.
+
+
+
+
+
+
+
+
+
+ Remove the deprecation mark from the application version.
+
+
+
+
+
+
+
+ LIST_PLATFORM
+
+ List the platforms known to the project. If the option
+ is specified, then only the short names of the platforms are listed.
+
+
+
+
+ ADD_PLATFORM
+
+ Add a new platform. NAME is the short name
+ of the platform that is used in the scheduler requests.
+ DESC is the user-friendly name that is
+ displayed to users.
+
+
+
+
+ DELETE_PLATFORM
+
+ Delete a platform. If the option is specified, the
+ command deletes all application versions for this platform. Otherwise, if there
+ are such application versions, the command will fail.
+
+
+
+
+ UPDATE_PLATFORM
+
+ Update the properties of a platform. The following properties can be modified:
+
+
+
+
+
+
+
+
+ Update the user-friendly description of the platform.
+
+
+
+
+
+
+
+
+
+ Indicate that the platform is deprecated. The feeder will then
+ ignore this platform when looking for new work.
+
+
+
+
+
+
+
+
+
+ Remove the deprecation mark from the platform.
+
+
+
+
+
+
+
+ ADD_STANDARD_PLATFORMS
+
+ Add all the standard platform definitions to the database that do not
+ exist yet.
+
+
+
+
+
diff --git a/py/Boinc/setup_project.py b/py/Boinc/setup_project.py
index 83b4943044..68c6d156e2 100644
--- a/py/Boinc/setup_project.py
+++ b/py/Boinc/setup_project.py
@@ -363,7 +363,7 @@ sys.path.insert(0, os.path.join('%s', 'py'))
'update_stats', 'db_dump', 'db_purge', 'show_shmem', 'census',
'delete_file', 'request_file_list', 'get_file', 'send_file' ])
map(lambda (s): install(srcdir('tools',s), dir('bin',s)),
- [ 'create_work', 'xadd', 'dbcheck_files_exist', 'run_in_ops',
+ [ 'appmgr', 'create_work', 'xadd', 'dbcheck_files_exist', 'run_in_ops',
'update_versions', 'parse_config', 'grep_logs', 'db_query',
'watch_tcp', 'sign_executable', 'dir_hier_move',
'dir_hier_path' ])
diff --git a/tools/appmgr b/tools/appmgr
new file mode 100755
index 0000000000..c82f3f5b0a
--- /dev/null
+++ b/tools/appmgr
@@ -0,0 +1,571 @@
+#! /usr/bin/env python
+#
+# Written by Gabor Gombas
+# Licensed under the same terms as the rest of BOINC.
+
+"""
+Manage applications and platforms from the command line.
+
+Run "appmgr --help" to list the available commands.
+Run "appmgr --help" to get the description of a command.
+"""
+
+
+import time, os, re, string
+from optparse import OptionParser
+from xml.dom.minidom import parseString
+
+import boinc_path_config
+from Boinc import database, db_mid, configxml
+
+# The list of standard platforms
+standard_platforms = {
+ 'windows_intelx86': 'Microsoft Windows (98 or later) running on an Intel x86-compatible CPU',
+ 'windows_x86_64': 'Microsoft Windows running on an AMD x86_64 or Intel EM64T CPU',
+ 'i686-pc-linux-gnu': 'Linux running on an Intel x86-compatible CPU',
+ 'x86_64-pc-linux-gnu': 'Linux running on an AMD x86_64 or Intel EM64T CPU',
+ 'powerpc-apple-darwin': 'Mac OS X 10.3 or later running on Motorola PowerPC',
+ 'i686-apple-darwin': 'Mac OS 10.4 or later running on Intel',
+ 'x86_64-apple-darwin': 'Intel 64-bit Mac OS 10.5 or later',
+ 'sparc-sun-solaris2.7': 'Solaris 2.7 running on a SPARC-compatible CPU',
+ 'sparc-sun-solaris': 'Solaris 2.8 or later running on a SPARC-compatible CPU',
+ 'sparc64-sun-solaris': 'Solaris 2.8 or later running on a SPARC 64-bit CPU',
+ 'powerpc64-ps3-linux-gnu': 'Sony Playstation 3 running Linux',
+ 'anonymous': 'anonymous'}
+
+def remove_directory(dir):
+ """Remove a directory and all files inside it."""
+
+ for file in os.listdir(dir):
+ os.unlink(os.path.join(dir, file))
+ os.rmdir(dir)
+
+def remove_app_directory(appver, config):
+ """Remove files for an app version under /apps"""
+
+ appdir = os.path.join(config.app_dir, appver.app.name)
+ for path in map(lambda file: os.path.join(appdir, file), os.listdir(appdir)):
+ match = re.match('^[^.]+_([0-9]+)[.]([0-9]+)_([^.]+?(?:[0-9][0-9.]*[0-9])?)(?:__([^.])+)?(?:[.][a-zA-Z0-9\_\-]*)?$', os.path.basename(path))
+ if not match:
+ continue
+
+ major, minor, platform_name, plan_class = match.groups()
+ version_num = int(major) * 100 + int(minor)
+ if plan_class is None:
+ plan_class = ''
+
+ if platform_name != appver.platform.name:
+ continue
+ if appver.version_num != version_num:
+ continue
+ if appver.plan_class != plan_class:
+ continue
+
+ if os.path.isdir(path):
+ remove_directory(path)
+ else:
+ os.unlink(path)
+
+ # If all versions are gone, remove the parent directory as well
+ try:
+ os.rmdir(appdir)
+ except:
+ pass
+
+def do_delete_app_version(appver):
+ """Delete both the files and the database entry for an app version."""
+
+ config = configxml.default_config().config
+
+ # Remove files/directories under app_dir
+ appdir = os.path.join(config.app_dir, appver.app.name)
+ if os.path.isdir(appdir):
+ remove_app_directory(appver, config)
+
+ # Remove files under download_dir
+ # appver.xml_doc does not have a parent node, so we have to add one
+ xml = parseString('' + appver.xml_doc + '')
+ for file in xml.getElementsByTagName('file_info'):
+ name = ''
+ for node in file.getElementsByTagName('name')[0].childNodes:
+ if node.nodeType == node.TEXT_NODE:
+ name += node.data
+ if not name:
+ continue
+ os.unlink(os.path.join(config.download_dir, name))
+
+ tmp = str(appver)
+ appver.remove()
+ print "Removed " + tmp
+
+def add_platform(args):
+ usage = """%prog add_platform
+
+Add a new platform definition to the database."""
+
+ parser = OptionParser(usage = usage)
+ options, extra_args = parser.parse_args(args)
+ if len(extra_args) != 2:
+ parser.error("Wrong number of arguments")
+
+ platform = database.Platform(create_time = time.time(),
+ name = extra_args[0],
+ user_friendly_name = extra_args[1],
+ deprecated = 0)
+ platform.commit()
+ print "Added " + str(platform)
+
+def del_platform(args):
+ usage = """%prog delete_platform
+
+Deletes a platform from the database. If the --force flag is specified, it also
+deletes all application versions for this platform. Otherwise, if there is an
+application version available for this platform, the command will fail."""
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("-f", "--force",
+ dest = "force",
+ action = "store_true",
+ default = False,
+ help = "remove any app versions that still exist for this platform")
+ options, extra_args = parser.parse_args(args)
+ if len(extra_args) != 1:
+ parser.error("Wrong number of arguments")
+
+ platforms = database.Platforms.find(name = extra_args[0])
+ if len(platforms) < 1:
+ raise SystemExit("Unknown platform name")
+ platform = platforms[0]
+
+ # If there are still application versions defined for this platform, then
+ # either bail out, or if --force was given, blow those application versions
+ # first
+ for appver in database.AppVersions.find(platform = platform):
+ if not options.force:
+ raise SystemExit("There are still application versions for this "
+ "platform, bailing out")
+ do_delete_app_version(appver)
+
+ # The id is gone after .remove(), so we have to save the stringified form
+ tmp = str(platform)
+ platform.remove()
+ print "Deleted " + tmp
+
+def update_platform(args):
+ usage = """%prog update_platform [options]
+
+Update platform data."""
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("--deprecated",
+ action = "store_const",
+ const = 1,
+ help = "mark the platform as deprecated")
+ parser.add_option("--no-deprecated",
+ dest = "deprecated",
+ action = "store_const",
+ const = 0,
+ help = "remove the deprecation mark")
+ parser.add_option("--user_friendly_name",
+ metavar = "DESC",
+ help = "set the user-friendly name of the platform")
+ options, extra_args = parser.parse_args(args)
+ if len(extra_args) != 1:
+ parser.error("Wrong number of arguments")
+
+ platforms = database.Platforms.find(name = extra_args[0])
+ if not platforms:
+ raise SystemExit("Unknown platform name")
+ platform = platforms[0]
+
+ if options.deprecated is not None:
+ platform.deprecated = options.deprecated
+ if options.user_friendly_name:
+ platform.user_friendly_name = options.user_friendly_name
+ platform.commit()
+ print "Updated " + str(platform)
+
+def list_platform(args):
+ usage = """%prog list_platform
+
+List all defined platforms."""
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("--short",
+ action = "store_true",
+ help = "show the short names only")
+ options, extra_args = parser.parse_args(args)
+ if extra_args:
+ parser.error("Extra arguments on the command line")
+
+ # Ensure consistent ordering
+ database.Platforms.select_args = 'ORDER BY name'
+ for platform in database.Platforms.find():
+ if options.short:
+ print platform.name
+ else:
+ desc = platform.name + ": " + platform.user_friendly_name
+ if platform.deprecated:
+ desc += " (Deprecated)"
+ print desc
+
+def add_standard_platforms(args):
+ usage = """%prog add_standard_platforms
+
+Add all standard platform definitions to the database. If some of them
+already exist they will not be modified."""
+
+ parser = OptionParser(usage = usage)
+ options, extra_args = parser.parse_args(args)
+ if extra_args:
+ parser.error("Extra arguments on the command line")
+
+ for name in standard_platforms.keys():
+ if database.Platforms.find(name = name):
+ continue
+ platform = database.Platform(create_time = time.time(),
+ name = name,
+ user_friendly_name = standard_platforms[name],
+ deprecated = 0)
+ platform.commit()
+ print "Added " + str(platform)
+
+def add_app(args):
+ usage = """%prog add [options]
+
+Register a new application in the database."""
+
+ hr_types = { 'no': 0, 'fine': 1, 'coarse': 2}
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("--hr",
+ choices = hr_types.keys(),
+ default = "no",
+ metavar = "(no|fine|coarse)",
+ help = "set the homogeneous redundancy type")
+ parser.add_option("--beta",
+ action = "store_const",
+ default = 0,
+ const = 1,
+ help = "the application is still in beta testing, and only "
+ "users participating in the test program will run it")
+ parser.add_option("--weight",
+ type = "float",
+ default = 1,
+ metavar = "NUM",
+ help = "set the weight of the application when application "
+ "interleaving is enabled in the feeder")
+ parser.add_option("--target_nresults",
+ type = "int",
+ default = 0,
+ metavar = "NUM",
+ help = "default number of replicas for adaptive replication")
+ options, extra_args = parser.parse_args(args)
+ if len(extra_args) != 2:
+ parser.error("Wrong number of arguments")
+
+ app = database.App(create_time = time.time(),
+ name = extra_args[0],
+ min_version = 0,
+ deprecated = 0,
+ user_friendly_name = extra_args[1],
+ homogeneous_redundancy = hr_types[options.hr],
+ weight = options.weight,
+ beta = options.beta,
+ target_nresults = options.target_nresults)
+ try:
+ app.commit()
+ except Exception, e:
+ print "Failed to add application"
+ return
+
+ print "Added " + str(app)
+
+def del_app(args):
+ usage = """%prog delete [options]
+
+Delete application versions. This command deletes the matching database
+entries and all corresponding files under /apps and
+/download. If more than one of the --version, --platform and
+--plan_class options are specified, they are AND'ed together. If no
+version/platform/plan class options are given, all versions of the application
+are deleted and the application is removed from the 'app' table as well."""
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("--version",
+ type = "float",
+ metavar = "VER",
+ help = "delete versions with the given version number")
+ parser.add_option("--platform",
+ metavar = "NAME",
+ help = "delete versions for the given platform")
+ parser.add_option("--plan_class",
+ metavar = "NAME",
+ help = "delete versions for the given plan class")
+ options, extra_args = parser.parse_args(args)
+ if len(extra_args) != 1:
+ parser.error("Wrong number of arguments")
+
+ apps = database.Apps.find(name = extra_args[0])
+ if not apps:
+ raise SystemExit("No such application")
+ app = apps[0]
+
+ kwargs = { 'app': app }
+ if options.version:
+ kwargs['version_num'] = int(options.version * 100)
+ if options.platform:
+ platforms = database.Platforms.find(name = options.platform)
+ if not platforms:
+ raise SystemExit("Unknown platform name")
+ kwargs['platform'] = platforms[0]
+ if options.plan_class:
+ kwargs['plan_class'] = options.plan_class
+
+ for appver in database.AppVersions.find(**kwargs):
+ do_delete_app_version(appver)
+
+ # If the user requested to remove all versions, then remove the entry
+ # from the 'app' table as well
+ if not options.version and not options.platform and not options.plan_class:
+ tmp = str(app)
+ app.remove()
+ print "Removed " + tmp
+
+def update_app(args):
+ usage = """%prog update [options]
+
+Update the properties of the given application."""
+
+ hr_types = { 'no': 0, 'fine': 1, 'coarse': 2}
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("--hr",
+ choices = hr_types.keys(),
+ default = "no",
+ metavar = "(no|fine|coarse)",
+ help = "set the homogeneous redundancy type")
+ parser.add_option("--beta",
+ action = "store_const",
+ const = 1,
+ help = "the application is still in beta testing, and only "
+ "users participating in the test program will run it")
+ parser.add_option("--no-beta",
+ action = "store_const",
+ dest = "beta",
+ const = 0,
+ help = "the application is no more in beta testing")
+ parser.add_option("--weight",
+ type = "float",
+ metavar = "NUM",
+ help = "set the weight of the application when application "
+ "interleaving is enabled in the feeder")
+ parser.add_option("--target_nresults",
+ type = "int",
+ metavar = "NUM",
+ help = "default number of replicas for adaptive replication")
+ parser.add_option("--user_friendly_name",
+ metavar = "DESC",
+ help = "set the user-friendly description of the application")
+ parser.add_option("--min_version",
+ type = "float",
+ metavar = "VER",
+ help = "set the minimum app version clients allowed to use")
+ parser.add_option("--deprecated",
+ action = "store_const",
+ const = 1,
+ help = "mark the application as deprecated")
+ parser.add_option("--no-deprecated",
+ dest = "deprecated",
+ action = "store_const",
+ const = 0,
+ help = "remove the deprecation mark")
+ options, extra_args = parser.parse_args(args)
+ if len(extra_args) != 1:
+ parser.error("Wrong number of arguments")
+
+ apps = database.Apps.find(name = extra_args[0])
+ if not apps:
+ raise SystemExit("No such application")
+ app = apps[0]
+
+ if options.hr is not None:
+ app.homogeneous_redundancy = hr_types[options.hr]
+ if options.beta is not None:
+ app.beta = options.beta
+ if options.weight is not None:
+ app.weight = options.weight
+ if options.target_nresults is not None:
+ app.target_nresults = options.target_nresults
+ if options.user_friendly_name:
+ app.user_friendly_name = options.user_friendly_name
+ if options.min_version is not None:
+ app.min_version = int(options.min_version * 100)
+ if options.deprecated is not None:
+ app.deprecated = options.deprecated
+
+ app.commit()
+ print "Updated " + str(app)
+
+def list_app(args):
+ usage = """%prog list
+
+List all applications and application versions."""
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("--no-versions",
+ action = "store_true",
+ default = False,
+ help = "do not list application versions")
+ options, extra_args = parser.parse_args(args)
+ if extra_args:
+ parser.error("Extra arguments on the command line")
+
+ # Ensure consistent ordering for the output
+ database.Apps.select_args = 'ORDER BY name'
+ # It would be nice to order by platform name...
+ database.AppVersions.select_args = 'ORDER BY version_num, platformid, plan_class'
+
+ hr_classes = {0: 'No', 1: 'Fine-grained', 2: 'Coarse'}
+
+ for app in database.Apps.find():
+ title = app.name + ": " + app.user_friendly_name
+ print title
+ print "-" * len(title)
+
+ props = []
+ if app.deprecated:
+ props.append("Deprecated.")
+ if app.beta:
+ props.append("Beta.")
+ if app.min_version:
+ props.append("Min. version: %.2f." % (float(app.min_version) / 100))
+ props.append(hr_classes[app.homogeneous_redundancy] +
+ ' homogeneous redundancy.')
+ props.append("Weight %g." % app.weight)
+ if app.target_nresults:
+ props.append(str(app.target_nresults) + " adaptive replicas.")
+ print string.join(props, ' ')
+
+ if options.no_versions:
+ print
+ continue
+
+ print "Versions:\n"
+ for appver in database.AppVersions.find(app = app):
+ desc = "\t%5.2f" % (float(appver.version_num) / 100)
+ desc += " " + appver.platform.name
+ props = []
+ if appver.plan_class:
+ props.append("Plan: " + appver.plan_class + ".")
+ if appver.deprecated:
+ props.append("Deprecated.")
+ if appver.min_core_version:
+ props.append("Min. core version: %g." % \
+ (float(appver.min_core_version) / 100))
+ if appver.max_core_version:
+ props.append("Max. core version: %g." % \
+ (float(appver.max_core_version) / 100))
+ if props:
+ desc += " (" + string.join(props, ' ') + ')'
+ print desc
+ print
+
+def update_appver(args):
+ usage = """%prog update_appver [options]
+
+Update the properties of the given application version(s). If none
+of the --version, --platform, --plan_class options are specified,
+then all versions for the given application are updated."""
+
+ parser = OptionParser(usage = usage)
+ parser.add_option("--version",
+ type = "float",
+ metavar = "VER",
+ help = "update just the given version number")
+ parser.add_option("--platform",
+ metavar = "NAME",
+ help = "update just the given platform")
+ parser.add_option("--plan_class",
+ metavar = "NAME",
+ help = "update just the given plan class")
+ parser.add_option("--min_core_version",
+ type = "float",
+ metavar = "VER",
+ help = "set the minimum usable core client version")
+ parser.add_option("--max_core_version",
+ type = "float",
+ metavar = "VER",
+ help = "set the maximum usable core client version")
+ parser.add_option("--deprecated",
+ action = "store_const",
+ const = 1,
+ help = "mark this version as deprecated")
+ parser.add_option("--no-deprecated",
+ dest = "deprecated",
+ action = "store_const",
+ const = 0,
+ help = "remove the deprecation mark")
+ options, extra_args = parser.parse_args(args)
+ if len(extra_args) != 1:
+ parser.error("Wrong number of arguments")
+
+ apps = database.Apps.find(name = extra_args[0])
+ if not apps:
+ raise SystemExit("No such application")
+ app = apps[0]
+
+ kwargs = { 'app': app }
+ if options.version:
+ kwargs['version_num'] = int(options.version * 100)
+ if options.platform:
+ platforms = database.Platforms.find(name = options.platform)
+ if not platforms:
+ raise SystemExit("Unknown platform name")
+ kwargs['platform'] = platforms[0]
+ if options.plan_class:
+ kwargs['plan_class'] = options.plan_class
+
+ for appver in database.AppVersions.find(**kwargs):
+ if options.deprecated is not None:
+ appver.deprecated = options.deprecated
+ if options.min_core_version is not None:
+ appver.min_core_version = int(options.min_core_version * 100)
+ if options.max_core_version is not None:
+ appver.max_core_version = int(options.max_core_version * 100)
+ appver.commit()
+ print "Updated " + str(appver)
+
+command_table = {
+ 'add': add_app,
+ 'delete': del_app,
+ 'update': update_app,
+ 'list': list_app,
+ 'update_appver': update_appver,
+ 'add_platform': add_platform,
+ 'delete_platform': del_platform,
+ 'update_platform': update_platform,
+ 'list_platform': list_platform,
+ 'add_standard_platforms': add_standard_platforms}
+
+usage = """%%prog [global options] [command arguments]
+
+Available commands:
+
+%s
+
+Use "%%prog --help" to get a description of the command.""" % \
+ string.join(map(lambda x: "\t" + x, sorted(command_table.keys())), "\n")
+
+parser = OptionParser(usage=usage)
+parser.disable_interspersed_args()
+
+global_options, args = parser.parse_args()
+if not args:
+ parser.error("Command is not provided")
+if not args[0] in command_table:
+ parser.error("Unknown command " + args[0])
+
+database.connect()
+
+command_table[args[0]](args[1:])
diff --git a/tools/pymw_setup b/tools/pymw_setup
deleted file mode 100644
index f3a7f15d02..0000000000
--- a/tools/pymw_setup
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/env python
-try:
- import boinc_path_config
- from Boinc import configxml, projectxml
-except:
- import configxml, projectxml
-from optparse import OptionParser
-import os, logging, sys, stat
-
-LINUX_WORKER = """\
-#!/bin/sh
-`python $1 $2 $3`
-`touch boinc_finish_called`
-"""
-
-WINDOWS_WORKER = """\
-@echo off
-set _path=%path%
-set _python=%_path:*python=python%
-set _python=%_python:~0,6%
-if %_python% neq python set PATH=%path%;C:\python25
-python %1 %2 %s3
-type nul > boinc_finish_called
-"""
-
-PYMW_APP = """\
-import sys
-import cPickle
-
-def pymw_get_input():
- infile = open(sys.argv[1], 'r')
- obj = cPickle.Unpickler(infile).load()
- infile.close()
- return obj
-
-def pymw_return_output(output):
- outfile = open(sys.argv[2], 'w')
- cPickle.Pickler(outfile).dump(output)
- outfile.close()
-"""
-
-FILE_REF = """\
-
-"""
-
-parser = OptionParser(usage="usage: %prog")
-parser.add_option("-p", "--pymw_dir", dest="pymw_dir", default="", help="specify the working directory of pymw", metavar="")
-options, args = parser.parse_args()
-
-logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
-
-pymw_dir = options.pymw_dir
-
-if pymw_dir == "":
- logging.error("py_mw working directory have to be provided (-p switch)")
- sys.exit(0)
-elif not os.path.exists(pymw_dir):
- logging.error(pymw_dir + " does not exist")
- sys.exit(0)
-
-logging.info("pymw working directory set to " + pymw_dir)
-
-logging.info("writing pymw_assimilator in config.xml")
-config = configxml.ConfigFile().read()
-
-# Remove old instances of pymw_assimilator
-for daemon in config.daemons:
- if daemon.cmd[0:16] == 'pymw_assimilator':
- config.daemons.remove_node(daemon)
-
-# Append new instance of pymw_assimilator to config.xml
-config.daemons.make_node_and_append("daemon").cmd = "pymw_assimilator -d 3 -app pymw -pymw_dir " + pymw_dir
-config.write()
-
-# Append new instance of pymw worker to project.xml
-project = projectxml.ProjectFile().read()
-found = False
-for element in project.elements:
- if element.name == 'pymw':
- logging.info("PyMW client applications are already installed")
- sys.exit(0)
-
-project.elements.make_node_and_append("app").name = "pymw"
-project.write()
-for element in project.elements:
- if element.name == "pymw":
- element.user_friendly_name = "PyMW - Master Worker Computing in Python"
- project.write()
-
-# Install worker applications
-app_dir = os.path.join(config.config.app_dir, "pymw")
-print app_dir
-if os.path.isdir(app_dir):
- logging.info("PyMW client applications are already installed")
-else:
- os.mkdir(app_dir)
- # Linux
- logging.info("setting up client application for Linux platform")
- linux_dir = os.path.join(app_dir, "pymw_1.00_i686-pc-linux-gnu")
- linux_exe = os.path.join(linux_dir, "pymw_1.00_i686-pc-linux-gnu")
- os.mkdir(linux_dir)
- open(linux_exe, "w").writelines(LINUX_WORKER)
- open(os.path.join(linux_dir, "pymw_1.00_i686-pc-linux-gnu.file_ref_info"), "w").writelines(FILE_REF)
- open(os.path.join(linux_dir, "pymw_app.py"), "w").writelines(PYMW_APP)
- open(os.path.join(linux_dir, "pymw_app.py.file_ref_info"), "w").writelines(FILE_REF)
- os.chmod(linux_exe, stat.S_IRWXU)
- logging.info("client application for Linux platform set up successfully")
-
- # Windows
- logging.info("setting up client application for Windows platform")
- win_dir = os.path.join(app_dir, "pymw_1.00_windows_intelx86")
- win_exe = os.path.join(win_dir, "pymw_1.00_windows_intelx86.exe")
- os.mkdir(win_dir)
- open(win_exe, "w").writelines(WINDOWS_WORKER)
- open(os.path.join(win_dir, "pymw_1.00_windows_intelx86.exe.file_ref_info"), "w").writelines(FILE_REF)
- open(os.path.join(win_dir, "pymw_app.py"), "w").writelines(PYMW_APP)
- open(os.path.join(win_dir, "pymw_app.py.file_ref_info"), "w").writelines(FILE_REF)
- os.chmod(win_exe, stat.S_IRWXU)
- logging.info("client application for Windows platform set up successfully")
-
- # Call update_versions
- project_home = config.config.app_dir.rpartition('/')[0]
- os.chdir(project_home)
- os.system("xadd")
- os.system("update_versions")
-logging.info("---------------------")
-logging.info("PyMW setup successful")
-logging.info("---------------------")