19aa5eb3d56fe5c70c0848afb75e7ac389409bf0
[sfa.git] / sfa / clientbin / sfaadmin.py
1 #!/usr/bin/python
2 import os
3 import sys
4 import copy
5 from pprint import pformat 
6 from sfa.generic import Generic
7 from optparse import OptionParser
8 from pprint import PrettyPrinter
9 from sfa.util.xrn import Xrn
10 from sfa.storage.record import Record 
11 from sfa.client.sfi import save_records_to_file
12 from sfa.trust.hierarchy import Hierarchy
13 from sfa.trust.gid import GID
14
15 pprinter = PrettyPrinter(indent=4)
16
17 def optparse_listvalue_callback(option, opt, value, parser):
18     setattr(parser.values, option.dest, value.split(','))
19
20 def args(*args, **kwargs):
21     def _decorator(func):
22         func.__dict__.setdefault('options', []).insert(0, (args, kwargs))
23         return func
24     return _decorator
25
26 class Commands(object):
27     def _get_commands(self):
28         available_methods = []
29         for attrib in dir(self):
30             if callable(getattr(self, attrib)) and not attrib.startswith('_'):
31                 available_methods.append(attrib)
32         return available_methods         
33
34
35 class RegistryCommands(Commands):
36     def __init__(self, *args, **kwds):
37         self.api= Generic.the_flavour().make_api(interface='registry')
38  
39     def version(self):
40         version = self.api.manager.GetVersion(self.api, {})
41         pprinter.pprint(version)
42
43     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn') 
44     @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None) 
45     def list(self, xrn, type=None):
46         xrn = Xrn(xrn, type) 
47         records = self.api.manager.List(self.api, xrn.get_hrn())
48         for record in records:
49             if not type or record['type'] == type:
50                 print "%s (%s)" % (record['hrn'], record['type'])
51
52
53     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn') 
54     @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None) 
55     @args('-o', '--outfile', dest='outfile', metavar='<outfile>', help='save record to file') 
56     @args('-f', '--format', dest='format', metavar='<display>', type='choice', 
57           choices=('text', 'xml', 'simple'), help='display record in different formats') 
58     def show(self, xrn, type=None, format=None, outfile=None):
59         records = self.api.manager.Resolve(self.api, xrn, type, True)
60         for record in records:
61             sfa_record = Record(dict=record)
62             sfa_record.dump(format) 
63         if outfile:
64             save_records_to_file(outfile, records)  
65
66
67     def _record_dict(self, xrn, type=None, url=None, key=None, \
68                      slices='', researchers=''):              
69         record_dict = {}
70         if xrn:
71             if type:
72                 xrn = Xrn(xrn, type)
73             else:
74                 xrn = Xrn(xrn)
75             record_dict['urn'] = xrn.get_urn()
76             record_dict['hrn'] = xrn.get_hrn()
77             record_dict['type'] = xrn.get_type()
78         if url:
79             record_dict['url'] = url
80         if key:
81             try:
82                 pubkey = open(key, 'r').read()
83             except IOError:
84                 pubkey = key
85             record_dict['keys'] = [pubkey]
86         if slices:
87             record_dict['slices'] = slices
88         if researchers:
89             record_dict['researchers'] = researchers
90         return record_dict
91
92     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn') 
93     @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None) 
94     @args('-u', '--url', dest='url', metavar='<url>', help='URL', default=None)
95     @args('-d', '--description', dest='description', metavar='<description>', 
96           help='Description', default=None)
97     @args('-k', '--key', dest='key', metavar='<key>', help='public key string or file', 
98           default=None)
99     @args('-s', '--slices', dest='slices', metavar='<slices>', help='slice xrns', 
100           default='', type="str", action='callback', callback=optparse_listvalue_callback)
101     @args('-r', '--researchers', dest='researchers', metavar='<researchers>', help='slice researchers', 
102           default='', type="str", action='callback', callback=optparse_listvalue_callback)
103     @args('-p', '--pis', dest='pis', metavar='<PIs>', 
104           help='Principal Investigators/Project Managers ', 
105           default='', type="str", action='callback', callback=optparse_listvalue_callback)
106     def register(self, xrn, type=None, url=None, description=None, key=None, slices='', 
107                  pis='', researchers=''):
108         record_dict = self._record_dict(xrn, type, url, key, slices, researchers)
109         self.api.manager.Register(self.api, record_dict)         
110
111
112     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn')
113     @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
114     @args('-u', '--url', dest='url', metavar='<url>', help='URL', default=None)
115     @args('-d', '--description', dest='description', metavar='<description>',
116           help='Description', default=None)
117     @args('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
118           default=None)
119     @args('-s', '--slices', dest='slices', metavar='<slices>', help='slice xrns',
120           default='', type="str", action='callback', callback=optparse_listvalue_callback)
121     @args('-r', '--researchers', dest='researchers', metavar='<researchers>', help='slice researchers',
122           default='', type="str", action='callback', callback=optparse_listvalue_callback)
123     @args('-p', '--pis', dest='pis', metavar='<PIs>',
124           help='Principal Investigators/Project Managers ',
125           default='', type="str", action='callback', callback=optparse_listvalue_callback)
126     def update(self, xrn, type=None, url=None, description=None, key=None, slices='', 
127                pis='', researchers=''):
128         record_dict = self._record_dict(xrn, type, url, key, slices, researchers)
129         self.api.manager.Update(self.api, record_dict)
130         
131     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn') 
132     @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None) 
133     def remove(self, xrn, type=None):
134         xrn = Xrn(xrn, type)
135         self.api.manager.Remove(self.api, xrn)            
136
137
138     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn') 
139     @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None) 
140     def credential(self, xrn, type=None):
141         cred = self.api.manager.GetCredential(self.api, xrn, type, self.api.hrn)
142         print cred
143    
144
145     def import_registry(self):
146         from sfa.importer import Importer
147         importer = Importer()
148         importer.run()
149     
150     @args('-a', '--all', dest='all', metavar='<all>', action='store_true', default=False,
151           help='Remove all registry records and all files in %s area' % Hierarchy().basedir)
152     @args('-c', '--certs', dest='certs', metavar='<certs>', action='store_true', default=False,
153           help='Remove all cached certs/gids found in %s' % Hierarchy().basedir )
154     @args('-0', '--no-reinit', dest='reinit', metavar='<reinit>', action='store_false', default=True,
155           help='Prevents new DB schema from being installed after cleanup')
156     def nuke(self, all=False, certs=False, reinit=True):
157         from sfa.storage.dbschema import DBSchema
158         from sfa.util.sfalogging import _SfaLogger
159         logger = _SfaLogger(logfile='/var/log/sfa_import.log', loggername='importlog')
160         logger.setLevelFromOptVerbose(self.api.config.SFA_API_LOGLEVEL)
161         logger.info("Purging SFA records from database")
162         dbschema=DBSchema()
163         dbschema.nuke()
164
165         # for convenience we re-create the schema here, so there's no need for an explicit
166         # service sfa restart
167         # however in some (upgrade) scenarios this might be wrong
168         if reinit:
169             logger.info("re-creating empty schema")
170             dbschema.init_or_upgrade()
171
172         # remove the server certificate and all gids found in /var/lib/sfa/authorities
173         if certs:
174             logger.info("Purging cached certificates")
175             for (dir, _, files) in os.walk('/var/lib/sfa/authorities'):
176                 for file in files:
177                     if file.endswith('.gid') or file == 'server.cert':
178                         path=dir+os.sep+file
179                         os.unlink(path)
180
181         # just remove all files that do not match 'server.key' or 'server.cert'
182         if all:
183             logger.info("Purging registry filesystem cache")
184             preserved_files = [ 'server.key', 'server.cert']
185             for (dir,_,files) in os.walk(Hierarchy().basedir):
186                 for file in files:
187                     if file in preserved_files: continue
188                     path=dir+os.sep+file
189                     os.unlink(path)
190         
191     
192 class CertCommands(Commands):
193     
194     def import_gid(self, xrn):
195         pass
196
197     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn')
198     @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
199     @args('-o', '--outfile', dest='outfile', metavar='<outfile>', help='output file', default=None)
200     def export(self, xrn, type=None, outfile=None):
201         from sfa.storage.alchemy import dbsession
202         from sfa.storage.model import RegRecord
203         hrn = Xrn(xrn).get_hrn()
204         request=dbsession.query(RegRecord).filter_by(hrn=hrn)
205         if type: request = request.filter_by(type=type)
206         record=request.first()
207         if record:
208             gid = GID(string=record.gid)
209         else:
210             # check the authorities hierarchy
211             hierarchy = Hierarchy()
212             try:
213                 auth_info = hierarchy.get_auth_info(hrn)
214                 gid = auth_info.gid_object
215             except:
216                 print "Record: %s not found" % hrn
217                 sys.exit(1)
218         # save to file
219         if not outfile:
220             outfile = os.path.abspath('./%s.gid' % gid.get_hrn())
221         gid.save_to_file(outfile, save_parents=True)
222         
223     @args('-g', '--gidfile', dest='gid', metavar='<gid>', help='path of gid file to display') 
224     def display(self, gidfile):
225         gid_path = os.path.abspath(gidfile)
226         if not gid_path or not os.path.isfile(gid_path):
227             print "No such gid file: %s" % gidfile
228             sys.exit(1)
229         gid = GID(filename=gid_path)
230         gid.dump(dump_parents=True)
231     
232
233 class AggregateCommands(Commands):
234
235     def __init__(self, *args, **kwds):
236         self.api= Generic.the_flavour().make_api(interface='aggregate')
237    
238     def version(self):
239         version = self.api.manager.GetVersion(self.api, {})
240         pprinter.pprint(version)
241
242     def slices(self):
243         print self.api.manager.ListSlices(self.api, [], {})
244
245     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn') 
246     def status(self, xrn):
247         urn = Xrn(xrn, 'slice').get_urn()
248         status = self.api.manager.SliverStatus(self.api, urn, [], {})
249         pprinter.pprint(status)
250  
251     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
252     @args('-r', '--rspec-version', dest='rspec_version', metavar='<rspec_version>', 
253           default='GENI', help='version/format of the resulting rspec response')  
254     def resources(self, xrn=None, rspec_version='GENI'):
255         options = {'geni_rspec_version': rspec_version}
256         if xrn:
257             options['geni_slice_urn'] = xrn
258         resources = self.api.manager.ListResources(self.api, [], options)
259         print resources
260         
261     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
262     @args('-r', '--rspec', dest='rspec', metavar='<rspec>', help='rspec file')  
263     @args('-u', '--user', dest='user', metavar='<user>', help='hrn/urn of slice user')  
264     @args('-k', '--key', dest='key', metavar='<key>', help="path to user's public key file")  
265     def create(self, xrn, rspec, user, key):
266         xrn = Xrn(xrn, 'slice')
267         slice_urn=xrn.get_urn()
268         rspec_string = open(rspec).read()
269         user_xrn = Xrn(user, 'user')
270         user_urn = user_xrn.get_urn()
271         user_key_string = open(key).read()
272         users = [{'urn': user_urn, 'keys': [user_key_string]}]
273         options={}
274         self.api.manager.CreateSliver(self, slice_urn, [], rspec_string, users, options) 
275
276     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
277     def delete(self, xrn):
278         self.api.manager.DeleteSliver(self.api, xrn, [], {})
279  
280     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
281     def start(self, xrn):
282         self.api.manager.start_slice(self.api, xrn, [])
283
284     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
285     def stop(self, xrn):
286         self.api.manager.stop_slice(self.api, xrn, [])      
287
288     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
289     def reset(self, xrn):
290         self.api.manager.reset_slice(self.api, xrn)
291
292
293     @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
294     @args('-r', '--rspec', dest='rspec', metavar='<rspec>', help='request rspec', default=None)
295     def ticket(self, xrn, rspec):
296         pass
297
298
299
300 class SliceManagerCommands(AggregateCommands):
301     
302     def __init__(self, *args, **kwds):
303         self.api= Generic.the_flavour().make_api(interface='slicemgr')
304
305
306 CATEGORIES = {'cert': CertCommands,
307               'registry': RegistryCommands,
308               'aggregate': AggregateCommands,
309               'slicemgr': SliceManagerCommands}
310
311 def category_usage():
312     print "Available categories:"
313     for k in CATEGORIES:
314         print "\t%s" % k
315
316 def main():
317     argv = copy.deepcopy(sys.argv)
318     script_name = argv.pop(0)
319     # ensure category is specified    
320     if len(argv) < 1:
321         print script_name + " category action [<args>]"
322         category_usage()
323         sys.exit(2)
324
325     # ensure category is valid
326     category = argv.pop(0)
327     usage = "%%prog %s action <args> [options]" % (category)
328     parser = OptionParser(usage=usage)
329     command_class =  CATEGORIES.get(category, None)
330     if not command_class:
331         print "no such category %s " % category
332         category_usage()
333         sys.exit(2)  
334     
335     # ensure command is valid      
336     command_instance = command_class()
337     actions = command_instance._get_commands()
338     if len(argv) < 1:
339         action = '__call__'
340     else:
341         action = argv.pop(0)
342     
343     if hasattr(command_instance, action):
344         command = getattr(command_instance, action)
345     else:
346         print script_name + " category action [<args>]"
347         print "Available actions for %s category:" % category
348         for k in actions:
349             print "\t%s" % k
350         sys.exit(2)
351
352     # ensure options are valid
353     options = getattr(command, 'options', [])
354     usage = "%%prog %s %s <args> [options]" % (category, action)
355     parser = OptionParser(usage=usage)
356     for arg, kwd in options:
357         print "arg", arg
358         print "kwd", kwd 
359         parser.add_option(*arg, **kwd)
360     (opts, cmd_args) = parser.parse_args(argv)
361     cmd_kwds = vars(opts)
362
363     # dont overrride meth
364     for k, v in cmd_kwds.items():
365         if v is None:
366             del cmd_kwds[k]
367
368     # execute commadn
369     try:
370         command(*cmd_args, **cmd_kwds)
371         sys.exit(0)
372     except TypeError:
373         print "Possible wrong number of arguments supplied"
374         print command.__doc__
375         parser.print_help()
376         #raise
377         raise
378     except Exception:
379         print "Command failed, please check log for more info"
380         raise
381
382
383 if __name__ == '__main__':
384     main()
385     
386      
387         
388