5 from pprint import pformat, PrettyPrinter
6 from optparse import OptionParser
8 from sfa.generic import Generic
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
15 pprinter = PrettyPrinter(indent=4)
17 def optparse_listvalue_callback(option, opt, value, parser):
18 setattr(parser.values, option.dest, value.split(','))
20 def args(*args, **kwargs):
22 func.__dict__.setdefault('options', []).insert(0, (args, kwargs))
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
35 class RegistryCommands(Commands):
36 def __init__(self, *args, **kwds):
37 self.api= Generic.the_flavour().make_api(interface='registry')
40 """Display the Registry version"""
41 version = self.api.manager.GetVersion(self.api, {})
42 pprinter.pprint(version)
44 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='authority to list (hrn/urn - mandatory)')
45 @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
46 @args('-r', '--recursive', dest='recursive', metavar='<recursive>', help='list all child records',
47 action='store_true', default=False)
48 def list(self, xrn, type=None, recursive=False):
49 """List names registered at a given authority - possibly filtered by type"""
51 options = {'recursive': recursive}
52 records = self.api.manager.List(self.api, xrn.get_hrn(), options=options)
53 for record in records:
54 if not type or record['type'] == type:
55 print "%s (%s)" % (record['hrn'], record['type'])
58 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
59 @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
60 @args('-o', '--outfile', dest='outfile', metavar='<outfile>', help='save record to file')
61 @args('-f', '--format', dest='format', metavar='<display>', type='choice',
62 choices=('text', 'xml', 'simple'), help='display record in different formats')
63 def show(self, xrn, type=None, format=None, outfile=None):
64 """Display details for a registered object"""
65 records = self.api.manager.Resolve(self.api, xrn, type, True)
66 for record in records:
67 sfa_record = Record(dict=record)
68 sfa_record.dump(format)
70 save_records_to_file(outfile, records)
73 def _record_dict(self, xrn=None, type=None, url=None, key=None, \
74 description=None, slices='', researchers=''):
81 record_dict['urn'] = xrn.get_urn()
82 record_dict['hrn'] = xrn.get_hrn()
83 record_dict['type'] = xrn.get_type()
85 record_dict['url'] = url
88 pubkey = open(key, 'r').read()
91 record_dict['keys'] = [pubkey]
93 record_dict['slices'] = slices
95 record_dict['researchers'] = researchers
97 record_dict['description'] = description
100 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
101 @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
102 @args('-u', '--url', dest='url', metavar='<url>', help='URL', default=None)
103 @args('-d', '--description', dest='description', metavar='<description>',
104 help='Description', default=None)
105 @args('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
107 @args('-s', '--slices', dest='slices', metavar='<slices>', help='slice xrns',
108 default='', type="str", action='callback', callback=optparse_listvalue_callback)
109 @args('-r', '--researchers', dest='researchers', metavar='<researchers>', help='slice researchers',
110 default='', type="str", action='callback', callback=optparse_listvalue_callback)
111 @args('-p', '--pis', dest='pis', metavar='<PIs>',
112 help='Principal Investigators/Project Managers ',
113 default='', type="str", action='callback', callback=optparse_listvalue_callback)
114 def register(self, xrn, type=None, url=None, description=None, key=None, slices='',
115 pis='', researchers=''):
116 """Create a new Registry recory"""
117 record_dict = self._record_dict(xrn=xrn, type=type, url=url, key=key,
118 slices=slices, researchers=researchers)
119 self.api.manager.Register(self.api, record_dict)
122 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
123 @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
124 @args('-u', '--url', dest='url', metavar='<url>', help='URL', default=None)
125 @args('-d', '--description', dest='description', metavar='<description>',
126 help='Description', default=None)
127 @args('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
129 @args('-s', '--slices', dest='slices', metavar='<slices>', help='slice xrns',
130 default='', type="str", action='callback', callback=optparse_listvalue_callback)
131 @args('-r', '--researchers', dest='researchers', metavar='<researchers>', help='slice researchers',
132 default='', type="str", action='callback', callback=optparse_listvalue_callback)
133 @args('-p', '--pis', dest='pis', metavar='<PIs>',
134 help='Principal Investigators/Project Managers ',
135 default='', type="str", action='callback', callback=optparse_listvalue_callback)
136 def update(self, xrn, type=None, url=None, description=None, key=None, slices='',
137 pis='', researchers=''):
138 """Update an existing Registry record"""
139 record_dict = self._record_dict(xrn=xrn, type=type, url=url, description=description,
140 key=key, slices=slices, researchers=researchers)
141 self.api.manager.Update(self.api, record_dict)
143 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
144 @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
145 def remove(self, xrn, type=None):
146 """Remove given object from the registry"""
148 self.api.manager.Remove(self.api, xrn)
151 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
152 @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
153 def credential(self, xrn, type=None):
154 """Invoke GetCredential"""
155 cred = self.api.manager.GetCredential(self.api, xrn, type, self.api.hrn)
159 def import_registry(self):
160 """Run the importer"""
161 from sfa.importer import Importer
162 importer = Importer()
165 @args('-a', '--all', dest='all', metavar='<all>', action='store_true', default=False,
166 help='Remove all registry records and all files in %s area' % Hierarchy().basedir)
167 @args('-c', '--certs', dest='certs', metavar='<certs>', action='store_true', default=False,
168 help='Remove all cached certs/gids found in %s' % Hierarchy().basedir )
169 @args('-0', '--no-reinit', dest='reinit', metavar='<reinit>', action='store_false', default=True,
170 help='Prevents new DB schema from being installed after cleanup')
171 def nuke(self, all=False, certs=False, reinit=True):
172 """Cleanup local registry DB, plus various additional filesystem cleanups optionally"""
173 from sfa.storage.dbschema import DBSchema
174 from sfa.util.sfalogging import _SfaLogger
175 logger = _SfaLogger(logfile='/var/log/sfa_import.log', loggername='importlog')
176 logger.setLevelFromOptVerbose(self.api.config.SFA_API_LOGLEVEL)
177 logger.info("Purging SFA records from database")
181 # for convenience we re-create the schema here, so there's no need for an explicit
182 # service sfa restart
183 # however in some (upgrade) scenarios this might be wrong
185 logger.info("re-creating empty schema")
186 dbschema.init_or_upgrade()
188 # remove the server certificate and all gids found in /var/lib/sfa/authorities
190 logger.info("Purging cached certificates")
191 for (dir, _, files) in os.walk('/var/lib/sfa/authorities'):
193 if file.endswith('.gid') or file == 'server.cert':
197 # just remove all files that do not match 'server.key' or 'server.cert'
199 logger.info("Purging registry filesystem cache")
200 preserved_files = [ 'server.key', 'server.cert']
201 for (dir,_,files) in os.walk(Hierarchy().basedir):
203 if file in preserved_files: continue
208 class CertCommands(Commands):
210 def import_gid(self, xrn):
213 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
214 @args('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
215 @args('-o', '--outfile', dest='outfile', metavar='<outfile>', help='output file', default=None)
216 def export(self, xrn, type=None, outfile=None):
217 """Fetch an object's GID from the Registry"""
218 from sfa.storage.alchemy import dbsession
219 from sfa.storage.model import RegRecord
220 hrn = Xrn(xrn).get_hrn()
221 request=dbsession.query(RegRecord).filter_by(hrn=hrn)
222 if type: request = request.filter_by(type=type)
223 record=request.first()
225 gid = GID(string=record.gid)
227 # check the authorities hierarchy
228 hierarchy = Hierarchy()
230 auth_info = hierarchy.get_auth_info(hrn)
231 gid = auth_info.gid_object
233 print "Record: %s not found" % hrn
237 outfile = os.path.abspath('./%s.gid' % gid.get_hrn())
238 gid.save_to_file(outfile, save_parents=True)
240 @args('-g', '--gidfile', dest='gid', metavar='<gid>', help='path of gid file to display (mandatory)')
241 def display(self, gidfile):
242 """Print contents of a GID file"""
243 gid_path = os.path.abspath(gidfile)
244 if not gid_path or not os.path.isfile(gid_path):
245 print "No such gid file: %s" % gidfile
247 gid = GID(filename=gid_path)
248 gid.dump(dump_parents=True)
251 class AggregateCommands(Commands):
253 def __init__(self, *args, **kwds):
254 self.api= Generic.the_flavour().make_api(interface='aggregate')
257 """Display the Aggregate version"""
258 version = self.api.manager.GetVersion(self.api, {})
259 pprinter.pprint(version)
262 """List the running slices at this Aggregate"""
263 print self.api.manager.ListSlices(self.api, [], {})
265 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
266 def status(self, xrn):
267 """Display the status of a slice or slivers"""
268 urn = Xrn(xrn, 'slice').get_urn()
269 status = self.api.manager.SliverStatus(self.api, urn, [], {})
270 pprinter.pprint(status)
272 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
273 @args('-r', '--rspec-version', dest='rspec_version', metavar='<rspec_version>',
274 default='GENI', help='version/format of the resulting rspec response')
275 def resources(self, xrn=None, rspec_version='GENI'):
276 """Display the available resources at an aggregate
277 or the resources allocated by a slice"""
278 options = {'geni_rspec_version': rspec_version}
280 options['geni_slice_urn'] = xrn
281 resources = self.api.manager.ListResources(self.api, [], options)
284 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='slice hrn/urn (mandatory)')
285 @args('-r', '--rspec', dest='rspec', metavar='<rspec>', help='rspec file (mandatory)')
286 @args('-u', '--user', dest='user', metavar='<user>', help='hrn/urn of slice user (mandatory)')
287 @args('-k', '--key', dest='key', metavar='<key>', help="path to user's public key file (mandatory)")
288 def create(self, xrn, rspec, user, key):
289 """Allocate slivers"""
290 xrn = Xrn(xrn, 'slice')
291 slice_urn=xrn.get_urn()
292 rspec_string = open(rspec).read()
293 user_xrn = Xrn(user, 'user')
294 user_urn = user_xrn.get_urn()
295 user_key_string = open(key).read()
296 users = [{'urn': user_urn, 'keys': [user_key_string]}]
298 self.api.manager.CreateSliver(self, slice_urn, [], rspec_string, users, options)
300 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='slice hrn/urn (mandatory)')
301 def delete(self, xrn):
303 self.api.manager.DeleteSliver(self.api, xrn, [], {})
305 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='slice hrn/urn (mandatory)')
306 def start(self, xrn):
308 self.api.manager.start_slice(self.api, xrn, [])
310 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='slice hrn/urn (mandatory)')
313 self.api.manager.stop_slice(self.api, xrn, [])
315 @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='slice hrn/urn (mandatory)')
316 def reset(self, xrn):
318 self.api.manager.reset_slice(self.api, xrn)
321 # @args('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn', default=None)
322 # @args('-r', '--rspec', dest='rspec', metavar='<rspec>', help='request rspec', default=None)
323 # def ticket(self, xrn, rspec):
328 class SliceManagerCommands(AggregateCommands):
330 def __init__(self, *args, **kwds):
331 self.api= Generic.the_flavour().make_api(interface='slicemgr')
336 CATEGORIES = {'certificate': CertCommands,
337 'registry': RegistryCommands,
338 'aggregate': AggregateCommands,
339 'slicemgr': SliceManagerCommands}
341 def find_category (self, string):
342 for (k,v) in SfaAdmin.CATEGORIES.items():
343 if k.startswith(string): return k
344 for (k,v) in SfaAdmin.CATEGORIES.items():
345 if k.find(string) >=1: return k
348 def summary_usage (self, category=None):
349 print "Usage:", self.script_name + " category action [<options>]"
350 if category and category in SfaAdmin.CATEGORIES:
351 categories=[category]
353 categories=SfaAdmin.CATEGORIES
355 cls=SfaAdmin.CATEGORIES[c]
356 print "==================== category=%s"%c
357 names=cls.__dict__.keys()
360 method=cls.__dict__[name]
361 if name.startswith('_'): continue
363 format="%%-%ds"%margin
365 doc=getattr(method,'__doc__',None)
367 print "<missing __doc__>"
369 lines=[line.strip() for line in doc.split("\n")]
372 for extra_line in lines: print margin*" ",extra_line
376 argv = copy.deepcopy(sys.argv)
377 self.script_name = argv.pop(0)
378 # ensure category is specified
382 # ensure category is valid
383 category_str = argv.pop(0)
384 category=self.find_category (category_str)
388 usage = "%%prog %s action [options]" % (category)
389 parser = OptionParser(usage=usage)
390 command_class = SfaAdmin.CATEGORIES.get(category, None)
391 if not command_class:
392 self.summary_usage(category)
394 # ensure command is valid
395 command_instance = command_class()
396 actions = command_instance._get_commands()
402 if hasattr(command_instance, action):
403 command = getattr(command_instance, action)
405 self.summary_usage(category)
407 # ensure options are valid
408 options = getattr(command, 'options', [])
409 usage = "%%prog %s %s [options]" % (category, action)
410 parser = OptionParser(usage=usage)
411 for arg, kwd in options:
412 parser.add_option(*arg, **kwd)
413 (opts, cmd_args) = parser.parse_args(argv)
414 cmd_kwds = vars(opts)
416 # dont overrride meth
417 for k, v in cmd_kwds.items():
423 # print "invoking %s *=%s **=%s"%(command.__name__,cmd_args, cmd_kwds)
424 command(*cmd_args, **cmd_kwds)
427 print "Possible wrong number of arguments supplied"
428 print command.__doc__
432 print "Command failed, please check log for more info"