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