#! /usr/bin/python # $Id: roundup-admin,v 1.6 2001-07-30 00:57:51 richard Exp $ import sys if int(sys.version[0]) < 2: print 'Roundup requires python 2.0 or later.' sys.exit(1) import string, os, getpass, getopt from roundup import date, roundupdb, init def usage(message=''): if message: message = 'Problem: '+message+'\n' commands = [] for command in figureCommands().values(): h = command.__doc__.split('\n')[0] commands.append(h[7:]) commands.sort() print '''%sUsage: roundup-admin [-i instance home] [-u login] [-c] Commands: %s Help: roundup-admin -h roundup-admin help -- this help roundup-admin help -- command-specific help roundup-admin morehelp -- even more detailed help '''%(message, '\n '.join(commands)) def moreusage(message=''): usage(message) print ''' All commands (except help) require an instance specifier. This is just the path to the roundup instance you're working with. It may be specified in the environment variable ROUNDUP_INSTANCE or on the command line as "-i instance". A designator is a classname and a nodeid concatenated, eg. bug1, user10, ... Property values are represented as strings in command arguments and in the printed results: . Strings are, well, strings. . Date values are printed in the full date format in the local time zone, and accepted in the full format or any of the partial formats explained below. . Link values are printed as node designators. When given as an argument, node designators and key strings are both accepted. . Multilink values are printed as lists of node designators joined by commas. When given as an argument, node designators and key strings are both accepted; an empty string, a single node, or a list of nodes joined by commas is accepted. When multiple nodes are specified to the roundup get or roundup set commands, the specified properties are retrieved or set on all the listed nodes. When multiple results are returned by the roundup get or roundup find commands, they are printed one per line (default) or joined by commas (with the -c) option. Where the command changes data, a login name/password is required. The login may be specified as either "name" or "name:password". . ROUNDUP_LOGIN environment variable . the -u command-line option If either the name or password is not supplied, they are obtained from the command-line. Date format examples: "2000-04-17.03:45" means "2000-04-17" means "01-25" means "08-13.22:13" means "11-07.09:32:43" means "14:25" means "8:47:11" means "." means "right now" Command help: ''' for name, command in figureCommands().items(): print '%s:'%name print ' ',command.__doc__ def do_init(instance_home, args): '''Usage: init [template [backend [admin password]]] Initialise a new Roundup instance. The command will prompt for the instance home directory (if not supplied through INSTANCE_HOME or the -i option. The template, backend and admin password may be specified on the command-line as arguments, in that order. ''' if len(argv) > n: template = argv[n] backend = argv[n+1] else: template = backend = '' # select template import roundup.templates templates = roundup.templates.listTemplates() print 'Templates:', ', '.join(templates) template = len(args) and args[0] or '' while template not in templates: template = raw_input('Select template [classic]: ').strip() if not template: template = 'classic' import roundup.backends backends = roundup.backends.__all__ backend = len(args) > 1 and args[1] or '' while backend not in backends: backend = raw_input('Select backend [anydbm]: ').strip() if not backend: backend = 'anydbm' if len(args) > 2: adminpw = confirm = args[2] else: adminpw = '' confirm = 'x' while adminpw != confirm: adminpw = getpass.getpass('Admin Password: ') confirm = getpass.getpass(' Confirm: ') init.init(instance_home, template, backend, adminpw) return 0 def do_get(db, args): '''Usage: get property designator[,designator]* Get the given property of one or more designator(s). Retrieves the property value of the nodes specified by the designators. ''' designators = string.split(args[0], ',') propname = args[1] # TODO: handle the -c option for designator in designators: classname, nodeid = roundupdb.splitDesignator(designator) print db.getclass(classname).get(nodeid, propname) return 0 def do_set(db, args): '''Usage: set designator[,designator]* propname=value ... Set the given property of one or more designator(s). Sets the property to the value for all designators given. ''' designators = string.split(args[0], ',') props = {} for prop in args[1:]: key, value = prop.split('=') props[key] = value for designator in designators: classname, nodeid = roundupdb.splitDesignator(designator) cl = db.getclass(classname) properties = cl.getprops() for key, value in props.items(): type = properties[key] if type.isStringType: continue elif type.isDateType: props[key] = date.Date(value) elif type.isIntervalType: props[key] = date.Interval(value) elif type.isLinkType: props[key] = value elif type.isMultilinkType: props[key] = value.split(',') apply(cl.set, (nodeid, ), props) return 0 def do_find(db, args): '''Usage: find classname propname=value ... Find the nodes of the given class with a given property value. Find the nodes of the given class with a given property value. ''' classname = args[0] cl = db.getclass(classname) # look up the linked-to class and get the nodeid that has the value propname, value = args[1:].split('=') propcl = cl[propname].classname nodeid = propcl.lookup(value) # now do the find # TODO: handle the -c option print cl.find(propname, nodeid) return 0 def do_spec(db, args): '''Usage: spec classname Show the properties for a classname. This lists the properties for a given class. ''' classname = args[0] cl = db.getclass(classname) for key, value in cl.properties.items(): print '%s: %s'%(key, value) def do_create(db, args): '''Usage: create classname property=value ... Create a new entry of a given class. This creates a new entry of the given class using the property name=value arguments provided on the command line after the "create" command. ''' classname = args[0] cl = db.getclass(classname) props = {} properties = cl.getprops() for prop in args[1:]: key, value = prop.split('=') type = properties[key] if type.isStringType: props[key] = value elif type.isDateType: props[key] = date.Date(value) elif type.isIntervalType: props[key] = date.Interval(value) elif type.isLinkType: props[key] = value elif type.isMultilinkType: props[key] = value.split(',') print apply(cl.create, (), props) return 0 def do_list(db, args): '''Usage: list classname [property] List the instances of a class. Lists all instances of the given class along. If the property is not specified, the "label" property is used. The label property is tried in order: the key, "name", "title" and then the first property, alphabetically. ''' db = instance.open() classname = args[0] cl = db.getclass(classname) if len(args) > 1: key = args[1] else: key = cl.labelprop() # TODO: handle the -c option for nodeid in cl.list(): value = cl.get(nodeid, key) print "%4s: %s"%(nodeid, value) return 0 def do_history(db, args): '''Usage: history designator Show the history entries of a designator. Lists the journal entries for the node identified by the designator. ''' classname, nodeid = roundupdb.splitDesignator(args[0]) # TODO: handle the -c option print db.getclass(classname).history(nodeid) return 0 def do_retire(db, args): '''Usage: retire designator[,designator]* Retire the node specified by designator. This action indicates that a particular node is not to be retrieved by the list or find commands, and its key value may be re-used. ''' designators = string.split(args[0], ',') for designator in designators: classname, nodeid = roundupdb.splitDesignator(designator) db.getclass(classname).retire(nodeid) return 0 def db_freshen(db, args): '''Usage: freshen Freshen an existing instance. **do not use This action should generally not be used. It reads in an instance database and writes it again. In the future, is may also update instance code to account for changes in templates. It's probably wise not to use it anyway. Until we're sure it won't break things... ''' for classname, cl in db.classes.items(): properties = cl.properties.keys() for nodeid in cl.list(): node = {} for name in properties: node[name] = cl.get(nodeid, name) db.setnode(classname, nodeid, node) return 0 def figureCommands(): d = {} for k, v in globals().items(): if k[:3] == 'do_': d[k[3:]] = v return d def main(): opts, args = getopt.getopt(sys.argv[1:], 'i:u:hc') # handle command-line args instance_home = os.environ.get('ROUNDUP_INSTANCE', '') name = password = '' if os.environ.has_key('ROUNDUP_LOGIN'): l = os.environ['ROUNDUP_LOGIN'].split(':') name = l[0] if len(l) > 1: password = l[1] comma_sep = 0 for opt, arg in opts: if opt == '-h': usage() return 0 if opt == '-i': instance_home = arg if opt == '-u': l = arg.split(':') name = l[0] if len(l) > 1: password = l[1] if opt == '-c': comma_sep = 1 # figure the command if not args: usage('No command specified') return 1 command = args[0] # handle help now if command == 'help': if len(args)>1: command = figureCommands().get(args[1], None) if not command: usage('no such command "%s"'%args[1]) return 1 print command.__doc__ return 0 usage() return 0 if command == 'morehelp': moreusage() return 0 # make sure we have an instance_home while not instance_home: instance_home = raw_input('Enter instance home: ').strip() # before we open the db, we may be doing an init if command == 'init': return do_init(instance_home, args) # open the database if command in ('create', 'set', 'retire'): while not name: name = raw_input('Login name: ') while not password: password = getpass.getpass(' password: ') # get the instance path, instance = os.path.split(instance_home) sys.path.insert(0, path) try: instance = __import__(instance) finally: del sys.path[0] command = figureCommands().get(command, None) # not a valid command if command is None: usage() return 1 db = instance.open(name or 'admin') try: return command(db, args[1:]) finally: db.close() return 1 if __name__ == '__main__': sys.exit(main()) # # $Log: not supported by cvs2svn $ # Revision 1.5 2001/07/30 00:04:48 richard # Made the "init" prompting more friendly. # # Revision 1.4 2001/07/29 07:01:39 richard # Added vim command to all source so that we don't get no steenkin' tabs :) # # Revision 1.3 2001/07/23 08:45:28 richard # ok, so now "./roundup-admin init" will ask questions in an attempt to get a # workable instance_home set up :) # _and_ anydbm has had its first test :) # # Revision 1.2 2001/07/23 08:20:44 richard # Moved over to using marshal in the bsddb and anydbm backends. # roundup-admin now has a "freshen" command that'll load/save all nodes (not # retired - mod hyperdb.Class.list() so it lists retired nodes) # # Revision 1.1 2001/07/23 03:46:48 richard # moving the bin files to facilitate out-of-the-boxness # # Revision 1.1 2001/07/22 11:15:45 richard # More Grande Splite stuff # # # vim: set filetype=python ts=4 sw=4 et si