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