2 from __future__ import with_statement
4 # sfi -- slice-based facility interface
9 from types import StringTypes, ListType
10 from optparse import OptionParser
12 from geni.util.certificate import Keypair, Certificate
13 from geni.util.credential import Credential
14 from geni.util.geniclient import GeniClient, ServerException
15 from geni.util.gid import create_uuid
16 from geni.util.record import *
17 from geni.util.rspec import Rspec
19 # xxx todo xxx auto-load ~/.sfi/sfi_config
21 sfi_dir = os.path.expanduser("~/.sfi/")
29 # Establish Connection to SliceMgr and Registry Servers
31 def set_servers(options):
38 if (options.sm is not None):
40 elif ("SFI_SM" in os.environ):
41 sm_url = os.environ["SFI_SM"]
43 print "No Known Slice Manager"
45 print " export SFI_SM=http://your.slicemanager.url:12347/"
46 print "Or add this argument to the command line:"
47 print " --slicemgr=http://your.slicemanager.url:12347/"
51 if (options.registry is not None):
52 reg_url = options.registry
53 elif ("SFI_REGISTRY" in os.environ):
54 reg_url = os.environ["SFI_REGISTRY"]
56 print "No Known Registry Server"
58 print " export SFI_REGISTRY=http://your.registry.url:12345/"
59 print "Or add this argument to the command line:"
60 print " --registry=http://your.registry.url:12345/"
64 print "Contacting Slice Manager at:", sm_url
65 print "Contacting Registry at:", reg_url
68 if (options.user is not None):
70 elif ("SFI_USER" in os.environ):
71 user = os.environ["SFI_USER"]
73 print "No Known User Name"
75 print " export SFI_USER=$SFI_AUTH.username"
76 print "Or add this argument to the command line:"
77 print " --user=username"
81 if (options.auth is not None):
82 authority = options.auth
83 elif ("SFI_AUTH" in os.environ):
84 authority = os.environ["SFI_AUTH"]
88 # Get key and certificate
89 key_file = get_key_file()
90 cert_file = get_cert_file(key_file)
92 # Establish connection to server(s)
93 slicemgr = GeniClient(sm_url, key_file, cert_file)
94 registry = GeniClient(reg_url, key_file, cert_file)
98 # Get various credential and spec files
100 # Establishes limiting conventions
101 # - conflates MAs and SAs
102 # - assumes last token in slice name is unique
104 # Bootstraps credentials
105 # - bootstrap user credential from self-signed certificate
106 # - bootstrap authority credential from user credential
107 # - bootstrap slice credential from user credential
111 parts = name.split(".")
115 file = os.path.join(sfi_dir, get_leaf(user) + ".pkey")
116 if (os.path.isfile(file)):
119 print "Key file", file, "does not exist"
123 def get_cert_file(key_file):
126 file = os.path.join(sfi_dir, get_leaf(user) + ".cert")
127 if (os.path.isfile(file)):
130 k = Keypair(filename = key_file)
131 cert = Certificate(subject=user)
133 cert.set_issuer(k, user)
136 print "Writing self-signed certificate to", file
137 cert.save_to_file(file)
143 file = os.path.join(sfi_dir, get_leaf(user) + ".cred")
144 if (os.path.isfile(file)):
145 user_cred = Credential(filename=file)
148 # bootstrap user credential
149 user_cred = registry.get_credential(None, "user", user)
151 user_cred.save_to_file(file, save_parents=True)
153 print "Writing user credential to", file
156 print "Failed to get user credential"
163 print "no authority specified. Use -a or set SF_AUTH"
166 file = os.path.join(sfi_dir, get_leaf("authority") +".cred")
167 if (os.path.isfile(file)):
168 auth_cred = Credential(filename=file)
171 # bootstrap authority credential from user credential
172 user_cred = get_user_cred()
173 auth_cred = registry.get_credential(user_cred, "sa", authority)
175 auth_cred.save_to_file(file, save_parents=True)
177 print "Writing authority credential to", file
180 print "Failed to get authority credential"
183 def get_slice_cred(name):
184 file = os.path.join(sfi_dir, "slice_" + get_leaf(name) + ".cred")
185 if (os.path.isfile(file)):
186 slice_cred = Credential(filename=file)
189 # bootstrap slice credential from user credential
190 user_cred = get_user_cred()
191 slice_cred = registry.get_credential(user_cred, "slice", name)
193 slice_cred.save_to_file(file, save_parents=True)
195 print "Writing slice credential to", file
198 print "Failed to get slice credential"
201 def delegate_cred(cred, hrn, type = 'authority'):
202 # the gid and hrn of the object we are delegating
203 object_gid = cred.get_gid_object()
204 object_hrn = object_gid.get_hrn()
205 cred.set_delegate(True)
206 if not cred.get_delegate():
207 raise Exception, "Error: Object credential %(object_hrn)s does not have delegate bit set" % locals()
210 records = registry.resolve(cred, hrn)
211 records = filter_records(type, records)
214 raise Exception, "Error: Didn't find a %(type)s record for %(hrn)s" % locals()
216 # the gid of the user who will be delegated too
217 delegee_gid = records[0].get_gid_object()
218 delegee_hrn = delegee_gid.get_hrn()
220 # the key and hrn of the user who will be delegating
221 user_key = Keypair(filename = get_key_file())
222 user_hrn = cred.get_gid_caller().get_hrn()
224 dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
225 dcred.set_gid_caller(delegee_gid)
226 dcred.set_gid_object(object_gid)
227 dcred.set_privileges(cred.get_privileges())
228 dcred.set_delegate(True)
229 dcred.set_pubkey(object_gid.get_pubkey())
230 dcred.set_issuer(user_key, user_hrn)
231 dcred.set_parent(cred)
237 def get_rspec_file(rspec):
238 if (os.path.isabs(rspec)):
241 file = os.path.join(sfi_dir, rspec)
242 if (os.path.isfile(file)):
245 print "No such rspec file", rspec
248 def get_record_file(record):
249 if (os.path.isabs(record)):
252 file = os.path.join(sfi_dir, record)
253 if (os.path.isfile(file)):
256 print "No such registry record file", record
259 def load_publickey_string(fn):
261 key_string = f.read()
263 # if the filename is a private key file, then extract the public key
264 if "PRIVATE KEY" in key_string:
265 outfn = tempfile.mktemp()
266 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
269 key_string = f.read()
274 # Generate sub-command parser
276 def create_cmd_parser(command, additional_cmdargs = None):
277 cmdargs = {"list": "name",
283 "resources": "[name]",
284 "create": "name rspec",
292 if additional_cmdargs:
293 cmdargs.update(additional_cmdargs)
295 if command not in cmdargs:
296 print "Invalid command\n"
298 for key in cmdargs.keys():
303 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
304 % (command, cmdargs[command]))
305 if command in ("resources"):
306 parser.add_option("-f", "--format", dest="format",type="choice",
307 help="display format (dns|ip|rspec)",default="rspec",
308 choices=("dns","ip","rspec"))
309 if command in ("list", "show", "remove"):
310 parser.add_option("-t", "--type", dest="type",type="choice",
311 help="type filter (user|slice|sa|ma|node|aggregate)",
312 choices=("user","slice","sa","ma","node","aggregate", "all"),
314 if command in ("show", "list", "resources"):
315 parser.add_option("-o", "--output", dest="file",
316 help="output XML to file", metavar="FILE", default=None)
317 if command in ("delegate"):
318 parser.add_option("-u", "--user",
319 action="store_true", dest="delegate_user", default=False,
320 help="delegate user credential")
321 parser.add_option("-s", "--slice", dest="delegate_slice",
322 help="delegate slice credential", metavar="HRN", default=None)
326 # Generate command line parser
327 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
328 description="Commands: list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
329 parser.add_option("-r", "--registry", dest="registry",
330 help="root registry", metavar="URL", default=None)
331 parser.add_option("-s", "--slicemgr", dest="sm",
332 help="slice manager", metavar="URL", default=None)
333 parser.add_option("-d", "--dir", dest="dir",
334 help="working directory", metavar="PATH", default = sfi_dir)
335 parser.add_option("-u", "--user", dest="user",
336 help="user name", metavar="HRN", default=None)
337 parser.add_option("-a", "--auth", dest="auth",
338 help="authority name", metavar="HRN", default=None)
339 parser.add_option("-v", "--verbose",
340 action="store_true", dest="verbose", default=False,
342 parser.disable_interspersed_args()
346 def dispatch(command, cmd_opts, cmd_args):
347 globals()[command](cmd_opts, cmd_args)
350 # Main: parse arguments and dispatch to command
355 parser = create_parser()
356 (options, args) = parser.parse_args()
359 print "No command given. Use -h for help."
363 (cmd_opts, cmd_args) = create_cmd_parser(command).parse_args(args[1:])
364 verbose = options.verbose
366 print "Resgistry %s, sm %s, dir %s, user %s, auth %s" % (options.registry,
371 print "Command %s" %command
372 if command in ("resources"):
373 print "resources cmd_opts %s" %cmd_opts.format
374 elif command in ("list","show","remove"):
375 print "cmd_opts.type %s" %cmd_opts.type
376 print "cmd_args %s" %cmd_args
381 dispatch(command, cmd_opts, cmd_args)
384 print "Command not found:", command
390 # Following functions implement the commands
392 # Registry-related commands
395 # list entires in named authority registry
396 def list(opts, args):
398 user_cred = get_user_cred()
399 list = registry.list(user_cred, args[0])
400 # filter on person, slice, site, node, etc.
401 # THis really should be in the filter_records funct def comment...
402 list = filter_records(opts.type, list)
404 print "%s (%s)" % (record['hrn'], record['type'])
406 save_records_to_file(opts.file, list)
409 # show named registry record
410 def show(opts, args):
412 user_cred = get_user_cred()
413 records = registry.resolve(user_cred, args[0])
414 records = filter_records(opts.type, records)
416 print "No record of type", opts.type
417 for record in records:
418 if record['type'] in ['user']:
419 record = UserRecord(dict = record)
420 elif record['type'] in ['slice']:
421 record = SliceRecord(dict = record)
422 elif record['type'] in ['node']:
423 record = NodeRecord(dict = record)
424 elif record['type'] in ['authority', 'ma', 'sa']:
425 record = AuthorityRecord(dict = record)
427 record = GeniRecord(dict = record)
431 save_records_to_file(opts.file, records)
434 def delegate(opts, args):
436 user_cred = get_user_cred()
437 if opts.delegate_user:
438 object_cred = user_cred
439 elif opts.delegate_slice:
440 object_cred = get_slice_cred(opts.delegate_slice)
442 print "Must specify either --user or --slice <hrn>"
445 # the gid and hrn of the object we are delegating
446 object_gid = object_cred.get_gid_object()
447 object_hrn = object_gid.get_hrn()
449 if not object_cred.get_delegate():
450 print "Error: Object credential", object_hrn, "does not have delegate bit set"
453 records = registry.resolve(user_cred, args[0])
454 records = filter_records("user", records)
457 print "Error: Didn't find a user record for", args[0]
460 # the gid of the user who will be delegated too
461 delegee_gid = records[0].get_gid_object()
462 delegee_hrn = delegee_gid.get_hrn()
464 # the key and hrn of the user who will be delegating
465 user_key = Keypair(filename = get_key_file())
466 user_hrn = user_cred.get_gid_caller().get_hrn()
468 dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
469 dcred.set_gid_caller(delegee_gid)
470 dcred.set_gid_object(object_gid)
471 dcred.set_privileges(object_cred.get_privileges())
472 dcred.set_delegate(True)
473 dcred.set_pubkey(object_gid.get_pubkey())
474 dcred.set_issuer(user_key, user_hrn)
475 dcred.set_parent(object_cred)
479 if opts.delegate_user:
480 dest_fn = os.path.join(sfi_dir, get_leaf(delegee_hrn) + "_" + get_leaf(object_hrn) + ".cred")
481 elif opts.delegate_slice:
482 dest_fn = os.path_join(sfi_dir, get_leaf(delegee_hrn) + "_slice_" + get_leaf(object_hrn) + ".cred")
484 dcred.save_to_file(dest_fn, save_parents = True)
486 print "delegated credential for", object_hrn, "to", delegee_hrn, "and wrote to", dest_fn
488 # removed named registry record
489 # - have to first retrieve the record to be removed
490 def remove(opts, args):
492 auth_cred = get_auth_cred()
493 return registry.remove(auth_cred, opts.type, args[0])
495 # add named registry record
498 auth_cred = get_auth_cred()
499 rec_file = get_record_file(args[0])
500 record = load_record_from_file(rec_file)
502 return registry.register(auth_cred, record)
504 # update named registry entry
505 def update(opts, args):
507 user_cred = get_user_cred()
508 rec_file = get_record_file(args[0])
509 record = load_record_from_file(rec_file)
510 if record.get_type() == "user":
511 if record.get_name() == user_cred.get_gid_object().get_hrn():
514 cred = get_auth_cred()
515 elif record.get_type() in ["slice"]:
517 cred = get_slice_cred(record.get_name())
518 except ServerException, e:
519 # XXX smbaker -- once we have better error return codes, update this
520 # to do something better than a string compare
521 if "Permission error" in e.args[0]:
522 cred = get_auth_cred()
525 elif record.get_type() in ["authority"]:
526 cred = get_auth_cred()
527 elif record.get_type() == 'node':
528 cred = get_auth_cred()
530 raise "unknown record type" + record.get_type()
531 return registry.update(cred, record)
534 # Slice-related commands
537 # list available nodes -- now use 'resources' w/ no argument instead
538 #def nodes(opts, args):
540 # user_cred = get_user_cred()
541 # if not opts.format:
544 # context = opts.format
545 # results = slicemgr.list_nodes(user_cred)
546 # if opts.format in ['rspec']:
547 # display_rspec(results)
549 # display_list(results)
550 # if (opts.file is not None):
551 # rspec = slicemgr.list_nodes(user_cred)
552 # save_rspec_to_file(rspec, opts.file)
555 # list instantiated slices
556 def slices(opts, args):
558 user_cred = get_user_cred()
559 results = slicemgr.get_slices(user_cred)
560 display_list(results)
563 # show rspec for named slice
564 def resources(opts, args):
567 slice_cred = get_slice_cred(args[0])
568 result = slicemgr.get_resources(slice_cred, args[0])
570 user_cred = get_user_cred()
571 result = slicemgr.get_resources(user_cred)
573 display_rspec(result, format)
574 if (opts.file is not None):
575 save_rspec_to_file(result, opts.file)
578 # created named slice with given rspec
579 def create(opts, args):
582 slice_cred = get_slice_cred(slice_hrn)
583 rspec_file = get_rspec_file(args[1])
584 with open(rspec_file) as f:
586 return slicemgr.create_slice(slice_cred, slice_hrn, rspec)
589 def delete(opts, args):
592 slice_cred = get_slice_cred(slice_hrn)
594 return slicemgr.delete_slice(slice_cred, slice_hrn)
597 def start(opts, args):
600 slice_cred = get_slice_cred(args[0])
601 return slicemgr.start_slice(slice_cred, slice_hrn)
604 def stop(opts, args):
607 slice_cred = get_slice_cred(args[0])
608 return slicemgr.stop_slice(slice_cred, slice_hrn)
611 def reset(opts, args):
614 slice_cred = get_slice_cred(args[0])
615 return slicemgr.reset_slice(slice_cred, slice_hrn)
619 # Display, Save, and Filter RSpecs and Records
620 # - to be replace by EMF-generated routines
624 def display_rspec(rspec, format = 'rspec'):
625 if format in ['dns']:
627 spec.parseString(rspec)
629 nodespecs = spec.getDictsByTagName('NodeSpec')
630 for nodespec in nodespecs:
631 if nodespec.has_key('name') and nodespec['name']:
632 if isinstance(nodespec['name'], ListType):
633 hostnames.extend(nodespec['name'])
634 elif isinstance(nodespec['name'], StringTypes):
635 hostnames.append(nodespec['name'])
637 elif format in ['ip']:
639 spec.parseString(rspec)
641 ifspecs = spec.getDictsByTagName('IfSpec')
642 for ifspec in ifspecs:
643 if ifspec.has_key('addr') and ifspec['addr']:
644 ips.append(ifspec['addr'])
652 def display_list(results):
653 for result in results:
656 def save_rspec_to_file(rspec, filename):
657 if not filename.startswith(os.sep):
658 filename = sfi_dir + filename
659 if not filename.endswith(".rspec"):
660 filename = filename + ".rspec"
662 f = open(filename, 'w')
667 def display_records(recordList, dump = False):
668 ''' Print all fields in the record'''
669 for record in recordList:
670 display_record(record, dump)
672 def display_record(record, dump = False):
676 info = record.getdict()
677 print "%s (%s)" % (info['hrn'], info['type'])
680 def filter_records(type, records):
681 filtered_records = []
682 for record in records:
683 if (record.get_type() == type) or (type == "all"):
684 filtered_records.append(record)
685 return filtered_records
687 def save_records_to_file(filename, recordList):
689 for record in recordList:
691 save_record_to_file(filename + "." + str(index), record)
693 save_record_to_file(filename, record)
696 def save_record_to_file(filename, record):
697 if not filename.startswith(os.sep):
698 filename = sfi_dir + filename
699 str = record.save_to_string()
700 file(filename, "w").write(str)
703 def load_record_from_file(filename):
704 str = file(filename, "r").read()
705 record = GeniRecord(string=str)
708 if __name__=="__main__":