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())
293 #
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)
299 #
300 # Revision 1.1 2001/07/23 03:46:48 richard
301 # moving the bin files to facilitate out-of-the-boxness
302 #
303 # Revision 1.1 2001/07/22 11:15:45 richard
304 # More Grande Splite stuff
305 #
306 #