Code

Added the Roundup spec to the new documentation directory.
[roundup.git] / roundup-admin
1 #! /usr/bin/python
3 # $Id: roundup-admin,v 1.3 2001-07-23 08:45:28 richard Exp $
5 import sys
6 if int(sys.version[0]) < 2:
7     print 'Roundup requires python 2.0 or later.'
8     sys.exit(1)
10 import string, os, getpass
11 from roundup import date, roundupdb, init
13 def determineLogin(instance, argv, n = 2):
14     name = password = ''
15     if argv[2] == '-u':
16         l = argv[3].split(':')
17         name = l[0]
18         if len(l) > 1:
19             password = l[1]
20         n = 4
21     elif os.environ.has_key('ROUNDUP_LOGIN'):
22         l = os.environ['ROUNDUP_LOGIN'].split(':')
23         name = l[0]
24         if len(l) > 1:
25             password = l[1]
26     while not name:
27         name = raw_input('Login name: ')
28     while not password:
29         password = getpass.getpass('  password: ')
30     # TODO use the password...
31     return n, instance.open(name)
33 def usage(message=''):
34     if message: message = 'Problem: '+message+'\n'
35     print '''%sUsage:
37  roundup [-i instance] init [template backend]
38    -- initialise the database
39  roundup [-i instance] spec classname
40    -- show the properties for a classname
41  roundup [-i instance] create [-u login] classname propname=value ...
42    -- create a new entry of a given class
43  roundup [-i instance] list [-c] classname
44    -- list the instances of a class
45  roundup [-i instance] history [-c] designator
46    -- show the history entries of a designator
47  roundup [-i instance] get [-c] designator[,designator,...] propname
48    -- get the given property of one or more designator(s)
49  roundup [-i instance] set [-u login] designator[,designator,...] propname=value ...
50    -- set the given property of one or more designator(s)
51  roundup [-i instance] find [-c] classname propname=value ...
52    -- find the class instances with a given property
53  roundup [-i instance] retire designator[,designator,...]
54    -- "retire" a designator
55  roundup help    
56    -- this help
57  roundup morehelp
58    -- even more detailed help
59 '''%message
61 def moreusage(message=''):
62     usage(message)
63     print '''
64 All commands (except help) require an instance specifier. This is just the path
65 to the roundup instance you're working with. It may be specified in the environment
66 variable ROUNDUP_INSTANCE or on the command line as "-i instance".
68 A designator is a classname and a nodeid concatenated, eg. bug1, user10, ...
70 Property values are represented as strings in command arguments and in the
71 printed results:
72  . Strings are, well, strings.
73  . Date values are printed in the full date format in the local time zone, and
74    accepted in the full format or any of the partial formats explained below.
75  . Link values are printed as node designators. When given as an argument,
76    node designators and key strings are both accepted.
77  . Multilink values are printed as lists of node designators joined by commas.
78    When given as an argument, node designators and key strings are both
79    accepted; an empty string, a single node, or a list of nodes joined by
80    commas is accepted.
82 When multiple nodes are specified to the roundup get or roundup set
83 commands, the specified properties are retrieved or set on all the listed
84 nodes. 
86 When multiple results are returned by the roundup get or roundup find
87 commands, they are printed one per line (default) or joined by commas (with
88 the -c) option. 
90 Where the command changes data, a login name/password is required. The
91 login may be specified as either "name" or "name:password".
92  . ROUNDUP_LOGIN environment variable
93  . the -u command-line option
94 If either the name or password is not supplied, they are obtained from the
95 command-line. 
97 Date format examples:
98   "2000-04-17.03:45" means <Date 2000-04-17.08:45:00>
99   "2000-04-17" means <Date 2000-04-17.00:00:00>
100   "01-25" means <Date yyyy-01-25.00:00:00>
101   "08-13.22:13" means <Date yyyy-08-14.03:13:00>
102   "11-07.09:32:43" means <Date yyyy-11-07.14:32:43>
103   "14:25" means <Date yyyy-mm-dd.19:25:00>
104   "8:47:11" means <Date yyyy-mm-dd.13:47:11>
105   "." means "right now"
106 '''
108 def main():
109     argv = sys.argv
111     if len(argv) == 1:
112         usage('No command specified')
113         return 1
115     # handle help now
116     if argv[1] == 'help':
117         usage()
118         return 0
119     if argv[1] == 'morehelp':
120         moreusage()
121         return 0
123     # figure the instance home
124     n = 1
125     if argv[1] == '-i':
126         if len(argv) == 2:
127             usage()
128             return 1
129         instance_home = argv[2]
130         n = 3
131     else:
132         instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
134     # now figure the command
135     command = argv[n]
136     n = n + 1
138     if command == 'init':
139         adminpw = ''
140         confirm = 'x'
141         if len(argv) > n:
142             template = argv[n]
143             backend = argv[n+1]
144         else:
145             template = backend = ''
146         while not instance_home:
147             instance_home = raw_input('Enter instance home: ').strip()
148         # TODO: list the templates
149         while not template:
150             template = raw_input('Select template: ').strip()
151         # TODO: list the backends
152         while not backend:
153             backend = raw_input('Select backend: ').strip()
154         while adminpw != confirm:
155             adminpw = getpass.getpass('Admin Password: ')
156             confirm = getpass.getpass('       Confirm: ')
157         init.init(instance_home, template, backend, adminpw)
158         return 0
160     # from here on, we need an instance_home
161     if not instance_home:
162         usage('No instance home specified')
163         return 1
165     # get the instance
166     path, instance = os.path.split(instance_home)
167     sys.path.insert(0, path)
168     try:
169         instance = __import__(instance)
170     finally:
171         del sys.path[0]
173     if command == 'get':
174         db = instance.open()
175         designators = string.split(argv[n], ',')
176         propname = argv[n+1]
177         # TODO: handle the -c option
178         for designator in designators:
179             classname, nodeid = roundupdb.splitDesignator(designator)
180             print db.getclass(classname).get(nodeid, propname)
182     elif command == 'set':
183         n, db = determineLogin(instance, argv, n)
184         designators = string.split(argv[n], ',')
185         props = {}
186         for prop in argv[n+1:]:
187             key, value = prop.split('=')
188             props[key] = value
189         for designator in designators:
190             classname, nodeid = roundupdb.splitDesignator(designator)
191             cl = db.getclass(classname)
192             properties = cl.getprops()
193             for key, value in props.items():
194                 type =  properties[key]
195                 if type.isStringType:
196                     continue
197                 elif type.isDateType:
198                     props[key] = date.Date(value)
199                 elif type.isIntervalType:
200                     props[key] = date.Interval(value)
201                 elif type.isLinkType:
202                     props[key] = value
203                 elif type.isMultilinkType:
204                     props[key] = value.split(',')
205             apply(cl.set, (nodeid, ), props)
207     elif command == 'find':
208         db = instance.open()
209         classname = argv[n]
210         cl = db.getclass(classname)
212         # look up the linked-to class and get the nodeid that has the value
213         propname, value = argv[n+1:].split('=')
214         propcl = cl[propname].classname
215         nodeid = propcl.lookup(value)
217         # now do the find
218         # TODO: handle the -c option
219         print cl.find(propname, nodeid)
221     elif command == 'spec':
222         db = instance.open()
223         classname = argv[n]
224         cl = db.getclass(classname)
225         for key, value in cl.properties.items():
226             print '%s: %s'%(key, value)
228     elif command == 'create':
229         n, db = determineLogin(instance, argv, n)
230         classname = argv[n]
231         cl = db.getclass(classname)
232         props = {}
233         properties = cl.getprops()
234         for prop in argv[n+1:]:
235             key, value = prop.split('=')
236             type =  properties[key]
237             if type.isStringType:
238                 props[key] = value 
239             elif type.isDateType:
240                 props[key] = date.Date(value)
241             elif type.isIntervalType:
242                 props[key] = date.Interval(value)
243             elif type.isLinkType:
244                 props[key] = value
245             elif type.isMultilinkType:
246                 props[key] = value.split(',')
247         print apply(cl.create, (), props)
249     elif command == 'list':
250         db = instance.open()
251         classname = argv[n]
252         cl = db.getclass(classname)
253         key = cl.getkey() or cl.properties.keys()[0]
254         # TODO: handle the -c option
255         for nodeid in cl.list():
256             value = cl.get(nodeid, key)
257             print "%4s: %s"%(nodeid, value)
259     elif command == 'history':
260         db = instance.open()
261         classname, nodeid = roundupdb.splitDesignator(argv[n])
262         # TODO: handle the -c option
263         print db.getclass(classname).history(nodeid)
265     elif command == 'retire':
266         n, db = determineLogin(instance, argv, n)
267         designators = string.split(argv[n], ',')
268         for designator in designators:
269             classname, nodeid = roundupdb.splitDesignator(designator)
270             db.getclass(classname).retire(nodeid)
272     elif command == 'freshen':
273         n, db = determineLogin(instance, argv, n)
274         for classname, cl in db.classes.items():
275             properties = cl.properties.keys()
276             for nodeid in cl.list():
277                 node = {}
278                 for name in properties:
279                     node[name] = cl.get(nodeid, name)
280                 db.setnode(classname, nodeid, node)
282     else:
283         print "Unknown command '%s'"%command
284         usage()
285         return 1
287     db.close()
288     return 0
290 if __name__ == '__main__':
291     sys.exit(main())
294 # $Log: not supported by cvs2svn $
295 # Revision 1.2  2001/07/23 08:20:44  richard
296 # Moved over to using marshal in the bsddb and anydbm backends.
297 # roundup-admin now has a "freshen" command that'll load/save all nodes (not
298 #  retired - mod hyperdb.Class.list() so it lists retired nodes)
300 # Revision 1.1  2001/07/23 03:46:48  richard
301 # moving the bin files to facilitate out-of-the-boxness
303 # Revision 1.1  2001/07/22 11:15:45  richard
304 # More Grande Splite stuff