</description>
</variable>
+ <variable id="data_dir" type="string">
+ <name>Data Directory </name>
+ <value>/var/lib/sfa/</value>
+ <description>Directory where cached certficiates and other data is stored.
+ </description>
+ </variable>
+
</variablelist>
</category>
--- /dev/null
+#!/usr/bin/python
+#
+# Script for basic access to the SFA configuration file store.
+#
+# $Id$
+#
+
+import sys
+import os
+import fcntl
+import getopt
+import signal
+from sfa.util.config import Config
+
+
+def usage():
+ print """
+Script to access the SFA configuration file store.
+
+Usage: %s [OPTION]... [FILES]
+ Conversion:
+
+ --shell Output shell configuration file
+ --python Output Python configuration file
+ --php Output PHP configuration file
+
+ Information:
+
+ --variables List names of all variables
+ --packages List names of all packages
+ --comps List comps.xml from configuration
+
+ Basic variable value manipulation:
+
+ --category= Category identifier
+ --variable= Variable identifier
+ --value= Variable value
+
+ Basic package list manipulation:
+
+ --group= Package group identifier
+ --package= Package name
+ --type= Package type
+
+ Miscellaneous:
+
+ -h, --help This message
+ -s, --save Save changes to first configuration file
+""".lstrip() % sys.argv[0]
+ sys.exit(1)
+
+
+def deprecated (message):
+ print "%s: deprecated usage"%sys.argv[0]
+ print message
+ sys.exit(1)
+
+def main():
+ config = Config()
+ fileobjs = []
+ output = None
+ category = {}
+ variable = {}
+ group = {}
+ package = {}
+ save = False
+
+ # Standard options
+ shortopts = "hs:"
+ longopts = ["shell", "bash", "python",
+ "php",
+ "xml",
+ "variables",
+ "packages",
+ "groups",
+ "comps",
+ "category=", "variable=", "value=",
+ "group=", "package=", "type=",
+ "help",
+ "save="]
+
+ try:
+ (opts, argv) = getopt.gnu_getopt(sys.argv[1:], shortopts, longopts)
+ except Exception, err:
+ sys.stderr.write("Error: " + str(err) + os.linesep)
+ sys.exit(1)
+
+ for (opt, optval) in opts:
+ if opt == "--shell" or \
+ opt == "--bash":
+ output = config.output_shell
+ elif opt == "--python":
+ output = config.output_python
+ elif opt == "--php":
+ output = config.output_php
+ elif opt == "--xml":
+ output = config.output_xml
+ elif opt == "--variables":
+ output = config.output_variables
+ elif opt == "--packages":
+ deprecated("option --packages deprecated -- use .lst files instead")
+ elif opt == "--groups":
+ deprecated("option --groups deprecated -- use .lst files instead")
+ elif opt == "--comps":
+ deprecated("option --comps deprecated -- use .lst files instead")
+ elif opt == "--category":
+ category['id'] = optval
+ elif opt == "--variable":
+ variable['id'] = optval
+ elif opt == "--value":
+ variable['value'] = optval
+ elif opt == "--group":
+# group['id'] = optval
+ deprecated("option --group deprecated -- use .lst files instead")
+ elif opt == "--package":
+# package['name'] = optval
+ deprecated("option --package deprecated -- use .lst files instead")
+ elif opt == "--type":
+ package['type'] = optval
+ elif opt == '-s' or opt == "--save":
+ if not optval:
+ usage()
+ print 'parsed save option',optval
+ save = optval
+ elif opt == '-h' or opt == "--help":
+ usage()
+
+ # Try the default
+ if not argv:
+ argv = ["/etc/sfa/sfa_config"]
+
+ # Merge all files
+ for file in argv:
+ try:
+ config.load(file)
+ except IOError:
+ pass
+ except Exception, err:
+ sys.stderr.write("Error: %s: %s" % (file, str(err)) + os.linesep)
+ sys.exit(1)
+
+ # --category, --variable, --value
+ if category.has_key('id') and variable.has_key('id'):
+ if variable.has_key('value'):
+ config.set(category['id'], variable['id'], variable['value'])
+ else:
+ value = config.get(category['id'], variable['id'])
+ print value
+
+ # --shell, --php, --xml
+ if output is not None:
+ sys.stdout.write(output())
+
+ # --save
+ if save:
+ # create directory if needed
+ # so that plc.d/{api,postgres} can create configs/site.xml
+ dirname = os.path.dirname (save)
+ if (not os.path.exists (dirname)):
+ os.makedirs(dirname,0755)
+ if (not os.path.exists (dirname)):
+ print "Cannot create dir %s - exiting" % dirname
+ sys.exit(1)
+ config.save(save)
+
+
+if __name__ == '__main__':
+ main()
-#!/bin/env python
+#!/usr/bin/env python
+import os
import sys
+import re
+import time
+import traceback
+import types
import readline
-import plc_config
+from StringIO import StringIO
+from optparse import OptionParser
+
+from sfa.util.version import version_tag
+from sfa.util.config import Config
def validator(validated_variables):
pass
'validator':validator,
}
+
+# GLOBAL VARIABLES
+#
+g_configuration=None
+usual_variables=None
+config_dir=None
+service=None
+
+def noop_validator(validated_variables):
+ pass
+
+# historically we could also configure the devel pkg....
+def init_configuration ():
+ global g_configuration
+ global usual_variables, config_dir, service
+
+ usual_variables=g_configuration["usual_variables"]
+ config_dir=g_configuration["config_dir"]
+ service=g_configuration["service"]
+
+ global def_default_config, def_site_config, def_consolidated_config
+ def_default_config= "%s/default_config.xml" % config_dir
+ def_site_config = "%s/configs/site_config" % config_dir
+ def_consolidated_config = "%s/%s_config" % (config_dir, service)
+
+ global mainloop_usage
+ mainloop_usage= """Available commands:
+ Uppercase versions give variables comments, when available
+ u/U\t\t\tEdit usual variables
+ w\t\t\tWrite
+ r\t\t\tRestart %(service)s service
+ R\t\t\tReload %(service)s service (rebuild config files for sh, python....)
+ q\t\t\tQuit (without saving)
+ h/?\t\t\tThis help
+---
+ l/L [<cat>|<var>]\tShow Locally modified variables/values
+ s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
+ e/E [<cat>|<var>]\tEdit variables (all, in category, single)
+---
+ c\t\t\tList categories
+ v/V [<cat>|<var>]\tList Variables (all, in category, single)
+---
+Typical usage involves: u, [l,] w, r, q
+""" % globals()
+
+def usage ():
+ command_usage="%prog [options] [default-xml [site-xml [consolidated-xml]]]"
+ init_configuration ()
+ command_usage +="""
+\t default-xml defaults to %s
+\t site-xml defaults to %s
+\t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
+ return command_usage
+
+####################
+variable_usage= """Edit Commands :
+#\tShow variable comments
+.\tStops prompting, return to mainloop
+/\tCleans any site-defined value, reverts to default
+=\tShows default value
+>\tSkips to next category
+?\tThis help
+"""
+
+####################
+def get_value (config, category_id, variable_id):
+ value = config.get (category_id, variable_id)
+ return value
+
+def get_type (config, category_id, variable_id):
+ value = config.get (category_id, variable_id)
+ #return variable['type']
+ return str
+
+def get_current_value (cread, cwrite, category_id, variable_id):
+ # the value stored in cwrite, if present, is the one we want
+ try:
+ result=get_value (cwrite,category_id,variable_id)
+ except:
+ result=get_value (cread,category_id,variable_id)
+ return result
+
+# refrain from using plc_config's _sanitize
+def get_varname (config, category_id, variable_id):
+ varname = category_id +"_"+ variable_id
+ config.locate_varname(varname)
+ return varname
+
+# could not avoid using _sanitize here..
+def get_name_comments (config, cid, vid):
+ try:
+ (category, variable) = config.get (cid, vid)
+ (id, name, value, comments) = config._sanitize_variable (cid,variable)
+ return (name,comments)
+ except:
+ return (None,[])
+
+def print_name_comments (config, cid, vid):
+ (name,comments)=get_name_comments(config,cid,vid)
+ if name:
+ print "### %s" % name
+ if comments:
+ for line in comments:
+ print "# %s" % line
+ else:
+ print "!!! No comment associated to %s_%s" % (cid,vid)
+
+####################
+def list_categories (config):
+ result=[]
+ for section in config.sections():
+ result += [section]
+ return result
+
+def print_categories (config):
+ print "Known categories"
+ for cid in list_categories(config):
+ print "%s" % (cid.upper())
+
+####################
+def list_category (config, cid):
+ result=[]
+ for section in config.sections():
+ if section == cid.lower():
+ for (name,value) in config.items(section):
+ result += ["%s_%s" %(cid,name)]
+ return result
+
+def print_category (config, cid, show_comments=True):
+ cid=cid.lower()
+ CID=cid.upper()
+ vids=list_category(config,cid)
+ if (len(vids) == 0):
+ print "%s : no such category"%CID
+ else:
+ print "Category %s contains" %(CID)
+ for vid in vids:
+ print vid.upper()
+
+####################
+def consolidate (default_config, site_config, consolidated_config):
+ global service
+ try:
+ conso = Config(default_config)
+ conso.load (site_config)
+ conso.save (consolidated_config)
+ except Exception, inst:
+ print "Could not consolidate, %s" % (str(inst))
+ return
+ print ("Merged\n\t%s\nand\t%s\ninto\t%s"%(default_config,site_config,
+ consolidated_config))
+
+def reload_service ():
+ global service
+ os.system("set -x ; service %s reload" % service)
+
+####################
+def restart_service ():
+ global service
+ print ("==================== Stopping %s" % service)
+ os.system("service %s stop" % service)
+ print ("==================== Starting %s" % service)
+ os.system("service %s start" % service)
+
+####################
+def prompt_variable (cdef, cread, cwrite, category, variable,
+ show_comments, support_next=False):
+
+
+ category_id = category
+ variable_id = variable
+
+ while True:
+ default_value = get_value(cdef,category_id,variable_id)
+ variable_type = get_type(cdef,category_id,variable_id)
+ current_value = get_current_value(cread,cwrite,category_id, variable_id)
+ varname = get_varname (cread,category_id, variable_id)
+
+ if show_comments :
+ print_name_comments (cdef, category_id, variable_id)
+ prompt = "== %s : [%s] " % (varname,current_value)
+ try:
+ answer = raw_input(prompt).strip()
+ except EOFError :
+ raise Exception ('BailOut')
+ except KeyboardInterrupt:
+ print "\n"
+ raise Exception ('BailOut')
+
+ # no change
+ if (answer == "") or (answer == current_value):
+ return None
+ elif (answer == "."):
+ raise Exception ('BailOut')
+ elif (answer == "#"):
+ print_name_comments(cread,category_id,variable_id)
+ elif (answer == "?"):
+ print variable_usage.strip()
+ elif (answer == "="):
+ print ("%s defaults to %s" %(varname,default_value))
+ # revert to default : remove from cwrite (i.e. site-config)
+ elif (answer == "/"):
+ cwrite.delete(category_id,variable_id)
+ print ("%s reverted to %s" %(varname,default_value))
+ return
+ elif (answer == ">"):
+ if support_next:
+ raise Exception ('NextCategory')
+ else:
+ print "No support for next category"
+ else:
+ if cdef.validate_type(variable_type, answer):
+ cwrite.set(category_id, variable_id, answer)
+ return
+ else:
+ print "Not a valid value"
+
+def prompt_variables_all (cdef, cread, cwrite, show_comments):
+ try:
+ for (category_id, (category, variables)) in cread.variables().iteritems():
+ print ("========== Category = %s" % category_id.upper())
+ for variable in variables.values():
+ try:
+ newvar = prompt_variable (cdef, cread, cwrite, category, variable,
+ show_comments, True)
+ except Exception, inst:
+ if (str(inst) == 'NextCategory'): break
+ else: raise
+
+ except Exception, inst:
+ if (str(inst) == 'BailOut'): return
+ else: raise
+
+def prompt_variables_category (cdef, cread, cwrite, cid, show_comments):
+ cid=cid.lower()
+ CID=cid.upper()
+ try:
+ print ("========== Category = %s" % CID)
+ for vid in list_category(cdef,cid):
+ (category,variable) = cdef.locate_varname(vid.upper())
+ newvar = prompt_variable (cdef, cread, cwrite, category, variable,
+ show_comments, False)
+ except Exception, inst:
+ if (str(inst) == 'BailOut'): return
+ else: raise
+
+####################
+def show_variable (cdef, cread, cwrite,
+ category, variable,show_value,show_comments):
+ assert category.has_key('id')
+ assert variable.has_key('id')
+
+ category_id = category ['id']
+ variable_id = variable['id']
+
+ default_value = get_value(cdef,category_id,variable_id)
+ current_value = get_current_value(cread,cwrite,category_id,variable_id)
+ varname = get_varname (cread,category_id, variable_id)
+ if show_comments :
+ print_name_comments (cdef, category_id, variable_id)
+ if show_value:
+ print "%s = %s" % (varname,current_value)
+ else:
+ print "%s" % (varname)
+
+def show_variables_all (cdef, cread, cwrite, show_value, show_comments):
+ for (category_id, (category, variables)) in cread.variables().iteritems():
+ print ("========== Category = %s" % category_id.upper())
+ for variable in variables.values():
+ show_variable (cdef, cread, cwrite,
+ category, variable,show_value,show_comments)
+
+def show_variables_category (cdef, cread, cwrite, cid, show_value,show_comments):
+ cid=cid.lower()
+ CID=cid.upper()
+ print ("========== Category = %s" % CID)
+ for vid in list_category(cdef,cid):
+ (category,variable) = cdef.locate_varname(vid.upper())
+ show_variable (cdef, cread, cwrite, category, variable,
+ show_value,show_comments)
+
+####################
+re_mainloop_0arg="^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
+re_mainloop_1arg="^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
+matcher_mainloop_0arg=re.compile(re_mainloop_0arg)
+matcher_mainloop_1arg=re.compile(re_mainloop_1arg)
+
+def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config):
+ global service
+ while True:
+ try:
+ answer = raw_input("Enter command (u for usual changes, w to save, ? for help) ").strip()
+ except EOFError:
+ answer =""
+ except KeyboardInterrupt:
+ print "\nBye"
+ sys.exit()
+
+ if (answer == "") or (answer in "?hH"):
+ print mainloop_usage
+ continue
+ groups_parse = matcher_mainloop_0arg.match(answer)
+ command=None
+ if (groups_parse):
+ command = groups_parse.group('command')
+ arg=None
+ else:
+ groups_parse = matcher_mainloop_1arg.match(answer)
+ if (groups_parse):
+ command = groups_parse.group('command')
+ arg=groups_parse.group('arg')
+ if not command:
+ print ("Unknown command >%s< -- use h for help" % answer)
+ continue
+
+ show_comments=command.isupper()
+
+ mode='ALL'
+ if arg:
+ mode=None
+ arg=arg.lower()
+ variables=list_category (cdef,arg)
+ if len(variables):
+ # category_id as the category name
+ # variables as the list of variable names
+ mode='CATEGORY'
+ category_id=arg
+ arg=arg.upper()
+ (category,variable)=cdef.locate_varname(arg)
+ if variable:
+ # category/variable as output by locate_varname
+ mode='VARIABLE'
+ if not mode:
+ print "%s: no such category or variable" % arg
+ continue
+
+ if command in "qQ":
+ # todo check confirmation
+ return
+ elif command == "w":
+ try:
+ # Confirm that various constraints are met before saving file.
+ validate_variables = g_configuration.get('validate_variables',{})
+ validated_variables = cwrite.verify(cdef, cread, validate_variables)
+ validator = g_configuration.get('validator',noop_validator)
+ validator(validated_variables)
+ cwrite.save(site_config)
+ except:
+ print "Save failed due to a configuration exception:"
+ print traceback.print_exc()
+ print ("Could not save -- fix write access on %s" % site_config)
+ break
+ print ("Wrote %s" % site_config)
+ consolidate(default_config, site_config, consolidated_config)
+ print ("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" % \
+ (service,service))
+ elif command in "uU":
+ global usual_variables
+ try:
+ for varname in usual_variables:
+ (category,variable) = cdef.locate_varname(varname)
+ if not (category is None and variable is None):
+ prompt_variable(cdef, cread, cwrite, category, variable, False)
+ except Exception, inst:
+ if (str(inst) != 'BailOut'):
+ raise
+ elif command == "r":
+ restart_service()
+ elif command == "R":
+ reload_service()
+ elif command == "c":
+ print_categories(cread)
+ elif command in "eE":
+ if mode == 'ALL':
+ prompt_variables_all(cdef, cread, cwrite,show_comments)
+ elif mode == 'CATEGORY':
+ prompt_variables_category(cdef,cread,cwrite,category_id,show_comments)
+ elif mode == 'VARIABLE':
+ try:
+ prompt_variable (cdef,cread,cwrite,category,variable,
+ show_comments,False)
+ except Exception, inst:
+ if str(inst) != 'BailOut':
+ raise
+ elif command in "vVsSlL":
+ show_value=(command in "sSlL")
+ (c1,c2,c3) = (cdef, cread, cwrite)
+ if command in "lL":
+ (c1,c2,c3) = (cwrite,cwrite,cwrite)
+ if mode == 'ALL':
+ show_variables_all(c1,c2,c3,show_value,show_comments)
+ elif mode == 'CATEGORY':
+ show_variables_category(c1,c2,c3,category_id,show_value,show_comments)
+ elif mode == 'VARIABLE':
+ show_variable (c1,c2,c3,category,variable,show_value,show_comments)
+ else:
+ print ("Unknown command >%s< -- use h for help" % answer)
+
+
+####################
+# creates directory for file if not yet existing
+def check_dir (config_file):
+ dirname = os.path.dirname (config_file)
+ if (not os.path.exists (dirname)):
+ try:
+ os.makedirs(dirname,0755)
+ except OSError, e:
+ print "Cannot create dir %s due to %s - exiting" % (dirname,e)
+ sys.exit(1)
+
+ if (not os.path.exists (dirname)):
+ print "Cannot create dir %s - exiting" % dirname
+ sys.exit(1)
+ else:
+ print "Created directory %s" % dirname
+
+####################
+def optParserSetup(configuration):
+ parser = OptionParser(usage=usage(), version="%prog " + version_tag )
+ parser.set_defaults(config_dir=configuration['config_dir'],
+ service=configuration['service'],
+ usual_variables=configuration['usual_variables'])
+ parser.add_option("","--configdir",dest="config_dir",help="specify configuration directory")
+ parser.add_option("","--service",dest="service",help="specify /etc/init.d style service name")
+ parser.add_option("","--usual_variable",dest="usual_variables",action="append", help="add a usual variable")
+ return parser
+
+def main(command,argv,configuration):
+ global g_configuration
+ g_configuration=configuration
+
+ parser = optParserSetup(configuration)
+ (config,args) = parser.parse_args()
+ if len(args)>3:
+ parser.error("too many arguments")
+
+ configuration['service']=config.service
+ configuration['usual_variables']=config.usual_variables
+ configuration['config_dir']=config.config_dir
+ # add in new usual_variables defined on the command line
+ for usual_variable in config.usual_variables:
+ if usual_variable not in configuration['usual_variables']:
+ configuration['usual_variables'].append(usual_variable)
+
+ # intialize configuration
+ init_configuration()
+
+ (default_config,site_config,consolidated_config) = (def_default_config, def_site_config, def_consolidated_config)
+ if len(args) >= 1:
+ default_config=args[0]
+ if len(args) >= 2:
+ site_config=args[1]
+ if len(args) == 3:
+ consolidated_config=args[2]
+
+ for c in (default_config,site_config,consolidated_config):
+ check_dir (c)
+
+ try:
+ # the default settings only - read only
+ cdef = Config(default_config)
+
+ # in effect : default settings + local settings - read only
+ cread = Config(default_config)
+ except:
+ print traceback.print_exc()
+ print ("default config files %s not found, is myplc installed ?" % default_config)
+ return 1
+
+ # local settings only, will be modified & saved
+ config_filename = "%s/sfa_config" % config.config_dir
+ cwrite=Config(config_filename)
+ try:
+ cread.load(site_config)
+ cwrite.load(default_config)
+ cwrite.load(site_config)
+ except:
+ cwrite = Config()
+
+ mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config)
+ return 0
+
if __name__ == '__main__':
command=sys.argv[0]
argv = sys.argv[1:]
- plc_config.main(command,argv,configuration)
+ main(command,argv,configuration)
# source function library
. /etc/init.d/functions
-
# Default locations
PGDATA=/var/lib/pgsql/data
postgresql_conf=$PGDATA/postgresql.conf
pghba_conf=$PGDATA/pg_hba.conf
postgresql_sysconfig=/etc/sysconfig/pgsql
-# PLC consolidated (merged) config file
-plc_whole_config=/etc/planetlab/plc_config.xml
# SFA consolidated (merged) config file
-sfa_whole_config=/etc/sfa/sfa_config.xml
+sfa_whole_config=/etc/sfa/sfa_config
# SFA default config (read-only template)
sfa_default_config=/etc/sfa/default_config.xml
# SFA local (site-dependent) file
-sfa_local_config=/etc/sfa/configs/site.xml
+sfa_local_config=/etc/sfa/configs/site_config
+sfa_local_config_xml=/etc/sfa/configs/site_config.xml
# Source sfa shell config if present
-[ -f /etc/sfa/sfa_config ] && . /etc/sfa/sfa_config
+[ -f /etc/sfa/sfa_config.sh ] && . /etc/sfa/sfa_config.sh
# Export so that we do not have to specify -p to psql invocations
export PGPORT=$SFA_DB_PORT
files=( $sfa_default_config $sfa_local_config )
for file in "${files[@]}" ; do
if [ -n "$force" -o $file -nt $sfa_whole_config ] ; then
- tmp=$(mktemp /tmp/sfa_config.xml.XXXXXX)
- plc-config --xml "${files[@]}" >$tmp
+ tmp=$(mktemp /tmp/sfa_config.XXXXXX)
+ sfa-config --python "${files[@]}" >$tmp
if [ $? -eq 0 ] ; then
mv $tmp $sfa_whole_config
chmod 444 $sfa_whole_config
done
# Convert configuration to various formats
- if [ -n "$force" -o $sfa_whole_config -nt /etc/sfa/sfa_config ] ; then
- plc-config --shell $sfa_whole_config > /etc/sfa/sfa_config
+ if [ -f $sfa_local_config_xml ] ; then
+ sfa-config --python $sfa_local_config_xml > $sfa_local_config
+ rm $sfa_local_config_xml
+ fi
+ if [ -n "$force" -o $sfa_local_config -nt $sfa_whole_config ] ; then
+ sfa-config --python $sfa_default_config $sfa_local_config > $sfa_whole_config
fi
- if [ -n "$force" -o $sfa_whole_config -nt /etc/sfa/sfa_config.py ] ; then
- plc-config --python $sfa_whole_config > /etc/sfa/sfa_config.py
+ if [ -n "$force" -o $sfa_whole_config -nt /etc/sfa/sfa_config.sh ] ; then
+ sfa-config --shell $sfa_default_config $sfa_local_config > /etc/sfa/sfa_config.sh
fi
# if [ -n "$force" -o $sfa_whole_config -nt /etc/sfa/php/sfa_config.php ] ; then
# mkdir -p /etc/sfa/php
#gen-sfa-cm-config.py
# reload the shell version
- [ -f /etc/sfa/sfa_config ] && . /etc/sfa/sfa_config
+ [ -f /etc/sfa/sfa_config.sh ] && . /etc/sfa/sfa_config.sh
}
function db_start () {
# only if enabled
- [ "$SFA_DB_ENABLED" == 1 ] || return
+ [ "$SFA_DB_ENABLED" == 1 -o "$SFA_DB_ENABLED" == True ] || return
if ! rpm -q myplc >& /dev/null; then
######## compute a password if needed
if [ -z "$SFA_DB_PASSWORD" ] ; then
SFA_DB_PASSWORD=$(uuidgen)
- plc-config --category=sfa_db --variable=password --value="$SFA_DB_PASSWORD" --save=$sfa_local_config $sfa_local_config >& /dev/null
+ sfa-config --category=sfa_db --variable=password --value="$SFA_DB_PASSWORD" --save=$sfa_local_config $sfa_local_config >& /dev/null
reload force
fi
PLC_DB_USER=$(plc-config --category=plc_db --variable=user)
PLC_DB_PASSWORD=$(plc-config --category=plc_db --variable=password)
# store this as the SFA user/password
- plc-config --category=sfa_db --variable=user --value=$PLC_DB_USER --save=$sfa_local_config $sfa_local_config >& /dev/null
- plc-config --category=sfa_db --variable=password --value=$PLC_DB_PASSWORD --save=$sfa_local_config $sfa_local_config >& /dev/null
+ sfa-config --category=sfa_db --variable=user --value=$PLC_DB_USER --save=$sfa_local_config $sfa_local_config >& /dev/null
+ sfa-config --category=sfa_db --variable=password --value=$PLC_DB_PASSWORD --save=$sfa_local_config $sfa_local_config >& /dev/null
reload force
fi
check
fi
check
+ sfaadmin reg sync_db
MESSAGE=$"SFA: Checking for PostgreSQL server"
echo -n "$MESSAGE"
function db_stop () {
# only if enabled
- [ "$SFA_DB_ENABLED" == 1 ] || return
+ [ "$SFA_DB_ENABLED" == 1 -o "$SFA_DB_ENABLED" == True ] || return
# not too nice, but.. when co-located with myplc we'll let it start/stop postgresql
if ! rpm -q myplc >& /dev/null ; then
# install peer certs
action $"SFA: installing peer certs" daemon /usr/bin/sfa-start.py -t -d $OPTIONS
- [ "$SFA_REGISTRY_ENABLED" == 1 ] && action $"SFA: Registry" daemon /usr/bin/sfa-start.py -r -d $OPTIONS
+ [ "$SFA_REGISTRY_ENABLED" == 1 -o "$SFA_REGISTRY_ENABLED" == True ] && action $"SFA: Registry" daemon /usr/bin/sfa-start.py -r -d $OPTIONS
- [ "$SFA_AGGREGATE_ENABLED" == 1 ] && action $"SFA: Aggregate" daemon /usr/bin/sfa-start.py -a -d $OPTIONS
+ [ "$SFA_AGGREGATE_ENABLED" == 1 -o "$SFA_AGGREGATE_ENABLED" == True ] && action $"SFA: Aggregate" daemon /usr/bin/sfa-start.py -a -d $OPTIONS
- [ "$SFA_SM_ENABLED" == 1 ] && action "SFA: SliceMgr" daemon /usr/bin/sfa-start.py -s -d $OPTIONS
+ [ "$SFA_SM_ENABLED" == 1 -o "$SFA_SM_ENABLED" == True ] && action "SFA: SliceMgr" daemon /usr/bin/sfa-start.py -s -d $OPTIONS
[ "$SFA_FLASHPOLICY_ENABLED" == 1 ] && \
action "Flash Policy Server" daemon /usr/bin/sfa_flashpolicy.py --file="$SFA_FLASHPOLICY_CONFIG_FILE" --port=$SFA_FLASHPOLICY_PORT -d
scripts = glob("clientbin/*.py") + \
[
'config/sfa-config-tty',
+ 'config/sfa-config',
'config/gen-sfa-cm-config.py',
'sfa/server/sfa-start.py',
'sfa/server/sfa_component_setup.py',
Group: Applications/System
BuildRequires: make
-Requires: myplc-config
Requires: python >= 2.5
Requires: pyOpenSSL >= 0.7
Requires: m2crypto
%{_bindir}/sfaadmin
%{_bindir}/keyconvert.py*
%{_bindir}/sfa-config-tty
+%{_bindir}/sfa-config
%config /etc/sfa/default_config.xml
%config (noreplace) /etc/sfa/aggregates.xml
%config (noreplace) /etc/sfa/registries.xml
from sfa.importer import Importer
importer = Importer()
importer.run()
+
+ def sync_db(self):
+ """Initiailize or upgrade the db"""
+ from sfa.storage.dbschema import DBSchema
+ dbschema=DBSchema()
+ dbschema.init_or_upgrade
@args('-a', '--all', dest='all', metavar='<all>', action='store_true', default=False,
help='Remove all registry records and all files in %s area' % help_basedir)
import sys
import os,os.path
from datetime import datetime
+from sfa.util.xrn import Xrn
import sfa.util.sfalogging
# importing sfa.utils.faults does pull a lot of stuff
self.assert_private_key()
registry_proxy = SfaServerProxy (self.registry_url, self.private_key_filename(),
certificate_filename)
- credential_string=registry_proxy.GetSelfCredential (certificate_string, self.hrn, "user")
+ try:
+ credential_string=registry_proxy.GetSelfCredential (certificate_string, self.hrn, "user")
+ except:
+ # some urns hrns may replace non hierarchy delimiters '.' with an '_' instead of escaping the '.'
+ hrn = Xrn(self.hrn).get_hrn().replace('\.', '_')
+ credential_string=registry_proxy.GetSelfCredential (certificate_string, hrn, "user")
self.plain_write (output, credential_string)
self.logger.debug("SfaClientBootstrap: Wrote result of GetSelfCredential in %s"%output)
return output
# the expected filenames for the various pieces
def private_key_filename (self):
- return self.fullpath ("%s.pkey"%self.hrn)
+ return self.fullpath ("%s.pkey" % Xrn.unescape(self.hrn))
def self_signed_cert_filename (self):
return self.fullpath ("%s.sscert"%self.hrn)
def my_credential_filename (self):
import codecs
import pickle
import json
+import shutil
from lxml import etree
from StringIO import StringIO
from optparse import OptionParser
from pprint import PrettyPrinter
+from tempfile import mkstemp
from sfa.trust.certificate import Keypair, Certificate
from sfa.trust.gid import GID
####################
def read_config(self):
config_file = os.path.join(self.options.sfi_dir,"sfi_config")
+ shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
try:
- config = Config (config_file)
+ if Config.is_ini(config_file):
+ config = Config (config_file)
+ else:
+ # try upgrading from shell config format
+ fp, fn = mkstemp(suffix='sfi_config', text=True)
+ config = Config(fn)
+ # we need to preload the sections we want parsed
+ # from the shell config
+ config.add_section('sfi')
+ config.add_section('sface')
+ config.load(config_file)
+ # back up old config
+ shutil.move(config_file, shell_config_file)
+ # write new config
+ config.save(config_file)
+
except:
- self.logger.critical("Failed to read configuration file %s"%config_file)
- self.logger.info("Make sure to remove the export clauses and to add quotes")
- if self.options.verbose==0:
- self.logger.info("Re-run with -v for more details")
- else:
- self.logger.log_exc("Could not read config file %s"%config_file)
- sys.exit(1)
+ self.logger.critical("Failed to read configuration file %s"%config_file)
+ self.logger.info("Make sure to remove the export clauses and to add quotes")
+ if self.options.verbose==0:
+ self.logger.info("Re-run with -v for more details")
+ else:
+ self.logger.log_exc("Could not read config file %s"%config_file)
+ sys.exit(1)
errors = 0
# Set SliceMgr URL
elif hasattr(config, "SFI_REGISTRY"):
self.reg_url = config.SFI_REGISTRY
else:
- self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
+ self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
errors += 1
# Set user HRN
elif hasattr(config, "SFI_USER"):
self.user = config.SFI_USER
else:
- self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
+ self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
errors += 1
# Set authority HRN
if not os.path.isfile(client_bootstrap.private_key_filename()):
self.logger.info ("private key not found, trying legacy name")
try:
- legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
+ legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
self.logger.debug("legacy_private_key=%s"%legacy_private_key)
client_bootstrap.init_private_key_if_missing (legacy_private_key)
self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
for person in persons:
pubkeys = []
for key_id in person['key_ids']:
- key = keys_by_id[key_id]
- if key['key_type'] == 'ssh':
+ # by construction all the keys we fetched are ssh keys
+ # so gpg keys won't be in there
+ try:
+ key = keys_by_id[key_id]
pubkeys.append(key)
+ except:
+ self.logger.warning("Could not spot key %d - probably non-ssh"%key_id)
keys_by_person_id[person['person_id']] = pubkeys
# Get all plc nodes
nodes = shell.GetNodes( {'peer_id': None}, ['node_id', 'hostname', 'site_id'])
# maintain the list of PIs for a given site
site_record.reg_pis = site_pis
+ dbsession.commit()
# import slices
for slice_id in site['slice_ids']:
class KeystoneClient:
- def __init__(self, config=None):
+ def __init__(self, username=None, password=None, tenant=None, url=None, config=None):
if not config:
config = Config()
opts = parse_novarc(config.SFA_NOVA_NOVARC)
+ if username:
+ opts['OS_USERNAME'] = username
+ if password:
+ opts['OS_PASSWORD'] = password
+ if tenant:
+ opts['OS_TENANT_NAME'] = tenant
+ if url:
+ opts['OS_AUTH_URL'] = url
+ self.opts = opts
self.client = keystone_client.Client(username=opts.get('OS_USERNAME'),
password=opts.get('OS_PASSWORD'),
tenant_name=opts.get('OS_TENANT_NAME'),
auth_url=opts.get('OS_AUTH_URL'))
+
+ def connect(self, *args, **kwds):
+ self.__init__(*args, **kwds)
def __getattr__(self, name):
return getattr(self.client, name)
return getattr(self.client, name)
class NovaClient:
- def __init__(self, config=None):
+ def __init__(self, username=None, password=None, tenant=None, url=None, config=None):
if not config:
config = Config()
opts = parse_novarc(config.SFA_NOVA_NOVARC)
+ if username:
+ opts['OS_USERNAME'] = username
+ if password:
+ opts['OS_PASSWORD'] = password
+ if tenant:
+ opts['OS_TENANT_NAME'] = tenant
+ if url:
+ opts['OS_AUTH_URL'] = url
+ self.opts = opts
self.client = nova_client.Client(username=opts.get('OS_USERNAME'),
api_key=opts.get('OS_PASSWORD'),
project_id=opts.get('OS_TENANT_NAME'),
service_type='compute',
service_name='',
)
+
+ def connect(self, *args, **kwds):
+ self.__init__(*args, **kwds)
def __getattr__(self, name):
return getattr(self.client, name)
def __init__ (self, config):
Driver.__init__(self, config)
- self.shell = Shell(config)
+ self.shell = Shell(config=config)
self.cache=None
if config.SFA_AGGREGATE_CACHING:
if NovaDriver.cache is None:
for researcher in researchers:
name = Xrn(researcher).get_leaf()
user = self.shell.auth_manager.users.find(name=name)
+ self.shell.auth_manager.roles.add_user_role(user, 'Member', tenant)
self.shell.auth_manager.roles.add_user_role(user, 'user', tenant)
+
pis = sfa_record.get('pis', [])
for pi in pis:
return slices
# get data from db
- projs = self.shell.auth_manager.get_projects()
- slice_urns = [OSXrn(proj.name, 'slice').urn for proj in projs]
-
+ instance_urns = []
+ instances = self.shell.nova_manager.servers.findall()
+ for instance in instances:
+ if instance.name not in instance_urns:
+ instance_urns.append(OSXrn(instance.name, type='slice').urn)
+
# cache the result
if self.cache:
logger.debug ("OpenStackDriver.list_slices stores value in cache")
- self.cache.add('slices', slice_urns)
+ self.cache.add('slices', instance_urns)
- return slice_urns
+ return instance_urns
# first 2 args are None in case of resource discovery
def list_resources (self, slice_urn, slice_hrn, creds, options):
return rspec
def sliver_status (self, slice_urn, slice_hrn):
+ # update nova connection
+ tenant_name = OSXrn(xrn=slice_hrn, type='slice').get_tenant_name()
+ self.shell.nova_manager.connect(tenant=tenant_name)
+
# find out where this slice is currently running
project_name = hrn_to_os_slicename(slice_hrn)
- project = self.shell.auth_manager.get_project(project_name)
- instances = self.shell.db.instance_get_all_by_project(project_name)
+ instances = self.shell.nova_manager.servers.findall(name=project_name)
if len(instances) == 0:
raise SliverDoesNotExist("You have not allocated any slivers here")
if instances:
top_level_status = 'ready'
result['geni_urn'] = slice_urn
- result['plos_login'] = 'root'
+ result['plos_login'] = 'root'
+ # do we need real dates here?
result['plos_expires'] = None
+ result['geni_expires'] = None
resources = []
for instance in instances:
res = {}
# instances are accessed by ip, not hostname. We need to report the ip
# somewhere so users know where to ssh to.
- res['plos_hostname'] = instance.hostname
- res['plos_created_at'] = datetime_to_string(utcparse(instance.created_at))
- res['plos_boot_state'] = instance.vm_state
- res['plos_sliver_type'] = instance.instance_type.name
- sliver_id = Xrn(slice_urn).get_sliver_id(instance.project_id, \
- instance.hostname, instance.id)
+ res['geni_expires'] = None
+ #res['plos_hostname'] = instance.hostname
+ res['plos_created_at'] = datetime_to_string(utcparse(instance.created))
+ res['plos_boot_state'] = instance.status
+ res['plos_sliver_type'] = self.shell.nova_manager.flavors.find(id=instance.flavor['id']).name
+ sliver_id = Xrn(slice_urn).get_sliver_id(instance.id)
res['geni_urn'] = sliver_id
- if instance.vm_state == 'running':
+ if instance.status.lower() == 'active':
res['boot_state'] = 'ready'
res['geni_status'] = 'ready'
else:
def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, users, options):
aggregate = OSAggregate(self)
- rspec = RSpec(rspec_string)
- instance_name = hrn_to_os_slicename(slice_hrn)
-
+
# assume first user is the caller and use their context
# for the ec2/euca api connection. Also, use the first users
# key as the project key.
for user in users:
pubkeys.extend(user['keys'])
- aggregate.run_instances(instance_name, rspec_string, key_name, pubkeys)
+ rspec = RSpec(rspec_string)
+ instance_name = hrn_to_os_slicename(slice_hrn)
+ tenant_name = OSXrn(xrn=slice_hrn, type='slice').get_tenant_name()
+ aggregate.run_instances(instance_name, tenant_name, rspec_string, key_name, pubkeys)
return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
def delete_sliver (self, slice_urn, slice_hrn, creds, options):
aggregate = OSAggregate(self)
+ tenant_name = OSXrn(xrn=slice_hrn, type='slice').get_tenant_name()
project_name = hrn_to_os_slicename(slice_hrn)
- return aggregate.delete_instances(project_name)
+ return aggregate.delete_instances(project_name, tenant_name)
def update_sliver(self, slice_urn, slice_hrn, rspec, creds, options):
name = hrn_to_os_slicename(slice_hrn)
+ tenant_name = OSXrn(xrn=slice_hrn, type='slice').get_tenant_name()
aggregate = OSAggregate(self)
return aggregate.update_instances(name)
return 1
def stop_slice (self, slice_urn, slice_hrn, creds):
+ tenant_name = OSXrn(xrn=slice_hrn, type='slice').get_tenant_name()
name = OSXrn(xrn=slice_urn).name
aggregate = OSAggregate(self)
- return aggregate.stop_instances(name)
+ return aggregate.stop_instances(name, tenant_name)
def reset_slice (self, slice_urn, slice_hrn, creds):
raise SfaNotImplemented ("reset_slice not available at this interface")
from sfa.openstack.osxrn import OSXrn, hrn_to_os_slicename
from sfa.rspecs.version_manager import VersionManager
from sfa.openstack.security_group import SecurityGroup
+from sfa.server.threadmanager import ThreadManager
from sfa.util.sfalogging import logger
def pubkeys_to_user_data(pubkeys):
return zones
def get_slice_nodes(self, slice_xrn):
+ # update nova connection
+ tenant_name = OSXrn(xrn=slice_xrn, type='slice').get_tenant_name()
+ self.driver.shell.nova_manager.connect(tenant=tenant_name)
+
zones = self.get_availability_zones()
name = hrn_to_os_slicename(slice_xrn)
instances = self.driver.shell.nova_manager.servers.findall(name=name)
- rspec_nodes = []
+ node_dict = {}
for instance in instances:
- rspec_node = Node()
-
- #TODO: find a way to look up an instances availability zone in essex
- #if instance.availability_zone:
- # node_xrn = OSXrn(instance.availability_zone, 'node')
- #else:
- # node_xrn = OSXrn('cloud', 'node')
- node_xrn = instance.metatata.get('component_id')
+ # determine node urn
+ node_xrn = instance.metadata.get('component_id')
if not node_xrn:
- node_xrn = OSXrn('cloud', 'node')
+ node_xrn = OSXrn('cloud', type='node')
+ else:
+ node_xrn = OSXrn(xrn=node_xrn, type='node')
- rspec_node['component_id'] = node_xrn.urn
- rspec_node['component_name'] = node_xrn.name
- rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
+ if not node_xrn.urn in node_dict:
+ rspec_node = Node()
+ rspec_node['component_id'] = node_xrn.urn
+ rspec_node['component_name'] = node_xrn.name
+ rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
+ rspec_node['slivers'] = []
+ node_dict[node_xrn.urn] = rspec_node
+ else:
+ rspec_node = node_dict[node_xrn.urn]
+
+ if instance.metadata.get('client_id'):
+ rspec_node['client_id'] = instance.metadata.get('client_id')
+
flavor = self.driver.shell.nova_manager.flavors.find(id=instance.flavor['id'])
sliver = instance_to_sliver(flavor)
- rspec_node['slivers'] = [sliver]
+ rspec_node['slivers'].append(sliver)
image = self.driver.shell.image_manager.get_images(id=instance.image['id'])
if isinstance(image, list) and len(image) > 0:
image = image[0]
sliver['disk_image'] = [disk_image]
# build interfaces
- interfaces = []
+ rspec_node['services'] = []
+ rspec_node['interfaces'] = []
addresses = instance.addresses
+ # HACK: public ips are stored in the list of private, but
+ # this seems wrong. Assume pub ip is the last in the list of
+ # private ips until openstack bug is fixed.
+ if addresses.get('private'):
+ login = Login({'authentication': 'ssh-keys',
+ 'hostname': addresses.get('private')[-1]['addr'],
+ 'port':'22', 'username': 'root'})
+ service = Services({'login': login})
+ rspec_node['services'].append(service)
+
for private_ip in addresses.get('private', []):
if_xrn = PlXrn(auth=self.driver.hrn,
interface='node%s:eth0' % (instance.hostId))
interface['ips'] = [{'address': private_ip['addr'],
#'netmask': private_ip['network'],
'type': private_ip['version']}]
- interfaces.append(interface)
- rspec_node['interfaces'] = interfaces
+ rspec_node['interfaces'].append(interface)
# slivers always provide the ssh service
- rspec_node['services'] = []
for public_ip in addresses.get('public', []):
login = Login({'authentication': 'ssh-keys',
'hostname': public_ip['addr'],
service = Services({'login': login})
rspec_node['services'].append(service)
rspec_nodes.append(rspec_node)
- return rspec_nodes
+ return node_dict.values()
def get_aggregate_nodes(self):
zones = self.get_availability_zones()
return rspec_nodes
+ def create_tenant(self, tenant_name):
+ tenants = self.driver.shell.auth_manager.tenants.findall(name=tenant_name)
+ if not tenants:
+ self.driver.shell.auth_manager.tenants.create(tenant_name, tenant_name)
+ tenant = self.driver.shell.auth_manager.tenants.find(name=tenant_name)
+ else:
+ tenant = tenants[0]
+ return tenant
+
+
def create_instance_key(self, slice_hrn, user):
- key_name = "%s:%s" (slice_name, Xrn(user['urn']).get_hrn())
+ slice_name = Xrn(slice_hrn).leaf
+ user_name = Xrn(user['urn']).leaf
+ key_name = "%s_%s" % (slice_name, user_name)
pubkey = user['keys'][0]
key_found = False
existing_keys = self.driver.shell.nova_manager.keypairs.findall(name=key_name)
key_found = True
if not key_found:
- self.driver.shll.nova_manager.keypairs.create(key_name, pubkey)
+ self.driver.shell.nova_manager.keypairs.create(key_name, pubkey)
return key_name
- def run_instances(self, slicename, rspec, key_name, pubkeys):
+ def run_instances(self, instance_name, tenant_name, rspec, key_name, pubkeys):
#logger.debug('Reserving an instance: image: %s, flavor: ' \
# '%s, key: %s, name: %s' % \
# (image_id, flavor_id, key_name, slicename))
+ # make sure a tenant exists for this slice
+ tenant = self.create_tenant(tenant_name)
+
+ # add the sfa admin user to this tenant and update our nova client connection
+ # to use these credentials for the rest of this session. This emsures that the instances
+ # we create will be assigned to the correct tenant.
+ sfa_admin_user = self.driver.shell.auth_manager.users.find(name=self.driver.shell.auth_manager.opts['OS_USERNAME'])
+ user_role = self.driver.shell.auth_manager.roles.find(name='user')
+ admin_role = self.driver.shell.auth_manager.roles.find(name='admin')
+ self.driver.shell.auth_manager.roles.add_user_role(sfa_admin_user, admin_role, tenant)
+ self.driver.shell.auth_manager.roles.add_user_role(sfa_admin_user, user_role, tenant)
+ self.driver.shell.nova_manager.connect(tenant=tenant.name)
+
authorized_keys = "\n".join(pubkeys)
files = {'/root/.ssh/authorized_keys': authorized_keys}
rspec = RSpec(rspec)
if not instances:
continue
for instance in instances:
- metadata = {}
- flavor_id = self.driver.shell.nova_manager.flavors.find(name=instance['name'])
- image = instance.get('disk_image')
- if image and isinstance(image, list):
- image = image[0]
- image_id = self.driver.shell.nova_manager.images.find(name=image['name'])
- fw_rules = instance.get('fw_rules', [])
- group_name = self.create_security_group(slicename, fw_rules)
- metadata['security_groups'] = [group_name]
- metadata['component_id'] = node['component_id']
try:
+ metadata = {}
+ flavor_id = self.driver.shell.nova_manager.flavors.find(name=instance['name'])
+ image = instance.get('disk_image')
+ if image and isinstance(image, list):
+ image = image[0]
+ image_id = self.driver.shell.nova_manager.images.find(name=image['name'])
+ fw_rules = instance.get('fw_rules', [])
+ group_name = self.create_security_group(instance_name, fw_rules)
+ metadata['security_groups'] = group_name
+ if node.get('component_id'):
+ metadata['component_id'] = node['component_id']
+ if node.get('client_id'):
+ metadata['client_id'] = node['client_id']
self.driver.shell.nova_manager.servers.create(flavor=flavor_id,
image=image_id,
key_name = key_name,
- security_group = group_name,
+ security_groups = [group_name],
files=files,
meta=metadata,
- name=slicename)
+ name=instance_name)
except Exception, err:
logger.log_exc(err)
- def delete_instances(self, instance_name):
+ def delete_instances(self, instance_name, tenant_name):
+
+ def _delete_security_group(instance):
+ security_group = instance.metadata.get('security_groups', '')
+ if security_group:
+ manager = SecurityGroup(self.driver)
+ timeout = 10.0 # wait a maximum of 10 seconds before forcing the security group delete
+ start_time = time.time()
+ instance_deleted = False
+ while instance_deleted == False and (time.time() - start_time) < timeout:
+ inst = self.driver.shell.nova_manager.servers.findall(id=instance.id)
+ if not inst:
+ instance_deleted = True
+ time.sleep(.5)
+ manager.delete_security_group(security_group)
+
+ thread_manager = ThreadManager()
+ self.driver.shell.nova_manager.connect(tenant=tenant_name)
instances = self.driver.shell.nova_manager.servers.findall(name=instance_name)
- security_group_manager = SecurityGroup(self.driver)
for instance in instances:
- # deleate this instance's security groups
- for security_group in instance.metadata.get('security_groups', []):
- # dont delete the default security group
- if security_group != 'default':
- security_group_manager.delete_security_group(security_group)
# destroy instance
self.driver.shell.nova_manager.servers.delete(instance)
+ # deleate this instance's security groups
+ thread_manager.run(_delete_security_group, instance)
return 1
- def stop_instances(self, instance_name):
+
+ def stop_instances(self, instance_name, tenant_name):
+ self.driver.shell.nova_manager.connect(tenant=tenant_name)
instances = self.driver.shell.nova_manager.servers.findall(name=instance_name)
for instance in instances:
self.driver.shell.nova_manager.servers.pause(instance)
def get_tenant_name(self):
self._normalize()
- tenant_name = self.hrn
- tenant_name = ".".join(tenant_name.split('.')[1:])
+ tenant_name = self.hrn.replace('\.', '')
return tenant_name
def create_security_group(self, name):
try:
- self.client.security_groups.create(name=name, description="")
+ self.client.security_groups.create(name=name, description=name)
except Exception, ex:
logger.log_exc("Failed to add security group")
+ raise
def delete_security_group(self, name):
try:
- self.client.security_groups(name=name)
+ security_group = self.client.security_groups.find(name=name)
+ self.client.security_groups.delete(security_group.id)
except Exception, ex:
logger.log_exc("Failed to delete security group")
from_port, to_port = icmp_type[0], icmp_type[1]
group = self.client.security_groups.find(name=group_name)
- self.client.security_group_rules.create(
- group_id=group.id,
- ip_protocol=protocol,
- from_port=from_port,
- to_port=to_port,
- cidr_ip=cidr_ip,
- )
+ self.client.security_group_rules.create(group.id, \
+ protocol, from_port, to_port,cidr_ip)
except Exception, ex:
logger.log_exc("Failed to add rule to group %s" % group_name)
'id': group.id,
'from_port': from_port,
'to_port': to_port,
- 'cird_ip': ip,
+ 'cidr_ip': ip,
'ip_protocol':protocol,
}
rule = self.client.security_group_rules.find(**filter)
config = Config()
if has_nova:
# instantiate managers
- self.auth_manager = KeystoneClient(config)
- self.image_manager = GlanceClient(config)
- self.nova_manager = NovaClient(config)
+ self.auth_manager = KeystoneClient(config=config)
+ self.image_manager = GlanceClient(config=config)
+ self.nova_manager = NovaClient(config=config)
else:
logger.debug('nova access - REST')
raise SfaNotImplemented('nova access - Rest')
tags_filter=filter.copy()
geni_available = options.get('geni_available')
- if geni_available:
+ if geni_available == True:
filter['boot_state'] = 'boot'
filter.update({'peer_id': None})
rspec.xml.set('expires', datetime_to_string(utcparse(slice['expires'])))
if not options.get('list_leases') or options.get('list_leases') and options['list_leases'] != 'leases':
- nodes, links = self.get_nodes_and_links(slice_xrn, slice, slivers)
+ nodes, links = self.get_nodes_and_links(slice_xrn, slice, slivers, options)
rspec.version.add_nodes(nodes)
rspec.version.add_links(links)
# add sliver defaults
# Adding the list_leases option to the caching key
if options.get('list_leases'):
version_string = version_string + "_"+options.get('list_leases', 'default')
+
+ # Adding geni_available to caching key
+ if options.get('geni_available'):
+ version_string = version_string + "_" + str(options.get('geni_available'))
# look in cache first
if cached_requested and self.cache and not slice_hrn:
persons = self.shell.GetPersons(slice['person_ids'], ['key_ids'])
key_ids = [key_id for person in persons for key_id in person['key_ids']]
person_keys = self.shell.GetKeys(key_ids)
- keys = [key['key'] for key in keys]
+ keys = [key['key'] for key in person_keys]
user.update({'urn': slice_urn,
'login': slice['name'],
if tags:
for tag in tags:
SFAv1Sliver.add_sliver_attribute(sliver_elem, tag['tagname'], tag['value'])
- if sliver.get('sliver_id'):
- name = PlXrn(xrn=sliver.get('sliver_id')).pl_slicename()
- sliver_elem.set('name', name)
@staticmethod
def add_sliver_attribute(xml, name, value):
def add_link_requests(self, link_tuples, append=False):
PGv2Link.add_link_requests(self.xml.root, link_tuples, append)
+ # Leases
+
+ def get_leases(self, filter=None):
+ pass
+
+ def add_leases(self, leases, network = None, no_dupes=False):
+ pass
+
# Utility
def merge(self, in_rspec):
"""
def __init__ (self, encoding="utf-8", methods='sfa.methods',
- config = "/etc/sfa/sfa_config.py",
+ config = "/etc/sfa/sfa_config",
peer_cert = None, interface = None,
key_file = None, cert_file = None, cache = None):
self.key = Keypair(filename = key_file)
self.cert = Certificate(filename = cert_file)
#self.server = SecureXMLRPCServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
- self.server = ThreadedServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
+ self.server = ThreadedServer((ip, int(port)), SecureXMLRpcRequestHandler, key_file, cert_file)
self.server.interface=interface
self.trusted_cert_list = None
self.register_functions()
#print " X509_V_ERR_CERT_UNTRUSTED"
return 1
+ # ignore X509_V_ERR_CERT_SIGNATURE_FAILURE
+ if err == 7:
+ return 1
+
logger.debug(" error %s in verify_callback"%err)
return 0
It it very similar to SimpleXMLRPCServer but it uses HTTPS for transporting XML data.
"""
- logger.debug("SecureXMLRPCServer.__init__, server_address=%s, cert_file=%s"%(server_address,cert_file))
+ logger.debug("SecureXMLRPCServer.__init__, server_address=%s, cert_file=%s, key_file=%s"%(server_address,cert_file,key_file))
self.logRequests = logRequests
self.interface = None
self.key_file = key_file
-#----------------------------------------------------------------------
-# Copyright (c) 2008 Board of Trustees, Princeton University
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and/or hardware specification (the "Work") to
-# deal in the Work without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Work, and to permit persons to whom the Work
-# is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Work.
-#
-# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
-# IN THE WORK.
-#----------------------------------------------------------------------
-
-##
-# SFA uses two crypto libraries: pyOpenSSL and M2Crypto to implement
-# the necessary crypto functionality. Ideally just one of these libraries
-# would be used, but unfortunately each of these libraries is independently
-# lacking. The pyOpenSSL library is missing many necessary functions, and
-# the M2Crypto library has crashed inside of some of the functions. The
-# design decision is to use pyOpenSSL whenever possible as it seems more
-# stable, and only use M2Crypto for those functions that are not possible
-# in pyOpenSSL.
-#
-# This module exports two classes: Keypair and Certificate.
-##
-#
-
-import functools
-import os
-import tempfile
-import base64
-from tempfile import mkstemp
-
-from OpenSSL import crypto
-import M2Crypto
-from M2Crypto import X509
-
-from sfa.util.faults import CertExpired, CertMissingParent, CertNotSignedByParent
-from sfa.util.sfalogging import logger
-
-glo_passphrase_callback = None
-
-##
-# A global callback msy be implemented for requesting passphrases from the
-# user. The function will be called with three arguments:
-#
-# keypair_obj: the keypair object that is calling the passphrase
-# string: the string containing the private key that's being loaded
-# x: unknown, appears to be 0, comes from pyOpenSSL and/or m2crypto
-#
-# The callback should return a string containing the passphrase.
-
-def set_passphrase_callback(callback_func):
- global glo_passphrase_callback
-
- glo_passphrase_callback = callback_func
-
-##
-# Sets a fixed passphrase.
-
-def set_passphrase(passphrase):
- set_passphrase_callback( lambda k,s,x: passphrase )
-
-##
-# Check to see if a passphrase works for a particular private key string.
-# Intended to be used by passphrase callbacks for input validation.
-
-def test_passphrase(string, passphrase):
- try:
- crypto.load_privatekey(crypto.FILETYPE_PEM, string, (lambda x: passphrase))
- return True
- except:
- return False
-
-def convert_public_key(key):
- keyconvert_path = "/usr/bin/keyconvert.py"
- if not os.path.isfile(keyconvert_path):
- raise IOError, "Could not find keyconvert in %s" % keyconvert_path
-
- # we can only convert rsa keys
- if "ssh-dss" in key:
- raise Exception, "keyconvert: dss keys are not supported"
-
- (ssh_f, ssh_fn) = tempfile.mkstemp()
- ssl_fn = tempfile.mktemp()
- os.write(ssh_f, key)
- os.close(ssh_f)
-
- cmd = keyconvert_path + " " + ssh_fn + " " + ssl_fn
- os.system(cmd)
-
- # this check leaves the temporary file containing the public key so
- # that it can be expected to see why it failed.
- # TODO: for production, cleanup the temporary files
- if not os.path.exists(ssl_fn):
- raise Exception, "keyconvert: generated certificate not found. keyconvert may have failed."
-
- k = Keypair()
- try:
- k.load_pubkey_from_file(ssl_fn)
- return k
- except:
- logger.log_exc("convert_public_key caught exception")
- raise
- finally:
- # remove the temporary files
- if os.path.exists(ssh_fn):
- os.remove(ssh_fn)
- if os.path.exists(ssl_fn):
- os.remove(ssl_fn)
-
-
-##
-# Public-private key pairs are implemented by the Keypair class.
-# A Keypair object may represent both a public and private key pair, or it
-# may represent only a public key (this usage is consistent with OpenSSL).
-
-class Keypair:
- key = None # public/private keypair
- m2key = None # public key (m2crypto format)
-
- ##
- # Creates a Keypair object
- # @param create If create==True, creates a new public/private key and
- # stores it in the object
- # @param string If string!=None, load the keypair from the string (PEM)
- # @param filename If filename!=None, load the keypair from the file
-
- def __init__(self, create=False, string=None, filename=None):
- if create:
- self.create()
- if string:
- self.load_from_string(string)
- if filename:
- self.load_from_file(filename)
-
- ##
- # Create a RSA public/private key pair and store it inside the keypair object
-
- def create(self):
- self.key = crypto.PKey()
- self.key.generate_key(crypto.TYPE_RSA, 1024)
-
- ##
- # Save the private key to a file
- # @param filename name of file to store the keypair in
-
- def save_to_file(self, filename):
- open(filename, 'w').write(self.as_pem())
- self.filename=filename
-
- ##
- # Load the private key from a file. Implicity the private key includes the public key.
-
- def load_from_file(self, filename):
- self.filename=filename
- buffer = open(filename, 'r').read()
- self.load_from_string(buffer)
-
- ##
- # Load the private key from a string. Implicitly the private key includes the public key.
-
- def load_from_string(self, string):
- if glo_passphrase_callback:
- self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string, functools.partial(glo_passphrase_callback, self, string) )
- self.m2key = M2Crypto.EVP.load_key_string(string, functools.partial(glo_passphrase_callback, self, string) )
- else:
- self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string)
- self.m2key = M2Crypto.EVP.load_key_string(string)
-
- ##
- # Load the public key from a string. No private key is loaded.
-
- def load_pubkey_from_file(self, filename):
- # load the m2 public key
- m2rsakey = M2Crypto.RSA.load_pub_key(filename)
- self.m2key = M2Crypto.EVP.PKey()
- self.m2key.assign_rsa(m2rsakey)
-
- # create an m2 x509 cert
- m2name = M2Crypto.X509.X509_Name()
- m2name.add_entry_by_txt(field="CN", type=0x1001, entry="junk", len=-1, loc=-1, set=0)
- m2x509 = M2Crypto.X509.X509()
- m2x509.set_pubkey(self.m2key)
- m2x509.set_serial_number(0)
- m2x509.set_issuer_name(m2name)
- m2x509.set_subject_name(m2name)
- ASN1 = M2Crypto.ASN1.ASN1_UTCTIME()
- ASN1.set_time(500)
- m2x509.set_not_before(ASN1)
- m2x509.set_not_after(ASN1)
- # x509v3 so it can have extensions
- # prob not necc since this cert itself is junk but still...
- m2x509.set_version(2)
- junk_key = Keypair(create=True)
- m2x509.sign(pkey=junk_key.get_m2_pkey(), md="sha1")
-
- # convert the m2 x509 cert to a pyopenssl x509
- m2pem = m2x509.as_pem()
- pyx509 = crypto.load_certificate(crypto.FILETYPE_PEM, m2pem)
-
- # get the pyopenssl pkey from the pyopenssl x509
- self.key = pyx509.get_pubkey()
- self.filename=filename
-
- ##
- # Load the public key from a string. No private key is loaded.
-
- def load_pubkey_from_string(self, string):
- (f, fn) = tempfile.mkstemp()
- os.write(f, string)
- os.close(f)
- self.load_pubkey_from_file(fn)
- os.remove(fn)
-
- ##
- # Return the private key in PEM format.
-
- def as_pem(self):
- return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.key)
-
- ##
- # Return an M2Crypto key object
-
- def get_m2_pkey(self):
- if not self.m2key:
- self.m2key = M2Crypto.EVP.load_key_string(self.as_pem())
- return self.m2key
-
- ##
- # Returns a string containing the public key represented by this object.
-
- def get_pubkey_string(self):
- m2pkey = self.get_m2_pkey()
- return base64.b64encode(m2pkey.as_der())
-
- ##
- # Return an OpenSSL pkey object
-
- def get_openssl_pkey(self):
- return self.key
-
- ##
- # Given another Keypair object, return TRUE if the two keys are the same.
-
- def is_same(self, pkey):
- return self.as_pem() == pkey.as_pem()
-
- def sign_string(self, data):
- k = self.get_m2_pkey()
- k.sign_init()
- k.sign_update(data)
- return base64.b64encode(k.sign_final())
-
- def verify_string(self, data, sig):
- k = self.get_m2_pkey()
- k.verify_init()
- k.verify_update(data)
- return M2Crypto.m2.verify_final(k.ctx, base64.b64decode(sig), k.pkey)
-
- def compute_hash(self, value):
- return self.sign_string(str(value))
-
- # only informative
- def get_filename(self):
- return getattr(self,'filename',None)
-
- def dump (self, *args, **kwargs):
- print self.dump_string(*args, **kwargs)
-
- def dump_string (self):
- result=""
- result += "KEYPAIR: pubkey=%40s..."%self.get_pubkey_string()
- filename=self.get_filename()
- if filename: result += "Filename %s\n"%filename
- return result
-
-##
-# The certificate class implements a general purpose X509 certificate, making
-# use of the appropriate pyOpenSSL or M2Crypto abstractions. It also adds
-# several addition features, such as the ability to maintain a chain of
-# parent certificates, and storage of application-specific data.
-#
-# Certificates include the ability to maintain a chain of parents. Each
-# certificate includes a pointer to it's parent certificate. When loaded
-# from a file or a string, the parent chain will be automatically loaded.
-# When saving a certificate to a file or a string, the caller can choose
-# whether to save the parent certificates as well.
-
-class Certificate:
- digest = "md5"
-
- cert = None
- issuerKey = None
- issuerSubject = None
- parent = None
- isCA = None # will be a boolean once set
-
- separator="-----parent-----"
-
- ##
- # Create a certificate object.
- #
- # @param lifeDays life of cert in days - default is 1825==5 years
- # @param create If create==True, then also create a blank X509 certificate.
- # @param subject If subject!=None, then create a blank certificate and set
- # it's subject name.
- # @param string If string!=None, load the certficate from the string.
- # @param filename If filename!=None, load the certficiate from the file.
- # @param isCA If !=None, set whether this cert is for a CA
-
- def __init__(self, lifeDays=1825, create=False, subject=None, string=None, filename=None, isCA=None):
- self.data = {}
- if create or subject:
- self.create(lifeDays)
- if subject:
- self.set_subject(subject)
- if string:
- self.load_from_string(string)
- if filename:
- self.load_from_file(filename)
-
- # Set the CA bit if a value was supplied
- if isCA != None:
- self.set_is_ca(isCA)
-
- # Create a blank X509 certificate and store it in this object.
-
- def create(self, lifeDays=1825):
- self.cert = crypto.X509()
- # FIXME: Use different serial #s
- self.cert.set_serial_number(3)
- self.cert.gmtime_adj_notBefore(0) # 0 means now
- self.cert.gmtime_adj_notAfter(lifeDays*60*60*24) # five years is default
- self.cert.set_version(2) # x509v3 so it can have extensions
-
-
- ##
- # Given a pyOpenSSL X509 object, store that object inside of this
- # certificate object.
-
- def load_from_pyopenssl_x509(self, x509):
- self.cert = x509
-
- ##
- # Load the certificate from a string
-
- def load_from_string(self, string):
- # if it is a chain of multiple certs, then split off the first one and
- # load it (support for the ---parent--- tag as well as normal chained certs)
-
- string = string.strip()
-
- # If it's not in proper PEM format, wrap it
- if string.count('-----BEGIN CERTIFICATE') == 0:
- string = '-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----' % string
-
- # If there is a PEM cert in there, but there is some other text first
- # such as the text of the certificate, skip the text
- beg = string.find('-----BEGIN CERTIFICATE')
- if beg > 0:
- # skipping over non cert beginning
- string = string[beg:]
-
- parts = []
-
- if string.count('-----BEGIN CERTIFICATE-----') > 1 and \
- string.count(Certificate.separator) == 0:
- parts = string.split('-----END CERTIFICATE-----',1)
- parts[0] += '-----END CERTIFICATE-----'
- else:
- parts = string.split(Certificate.separator, 1)
-
- self.cert = crypto.load_certificate(crypto.FILETYPE_PEM, parts[0])
-
- # if there are more certs, then create a parent and let the parent load
- # itself from the remainder of the string
- if len(parts) > 1 and parts[1] != '':
- self.parent = self.__class__()
- self.parent.load_from_string(parts[1])
-
- ##
- # Load the certificate from a file
-
- def load_from_file(self, filename):
- file = open(filename)
- string = file.read()
- self.load_from_string(string)
- self.filename=filename
-
- ##
- # Save the certificate to a string.
- #
- # @param save_parents If save_parents==True, then also save the parent certificates.
-
- def save_to_string(self, save_parents=True):
- string = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)
- if save_parents and self.parent:
- string = string + self.parent.save_to_string(save_parents)
- return string
-
- ##
- # Save the certificate to a file.
- # @param save_parents If save_parents==True, then also save the parent certificates.
-
- def save_to_file(self, filename, save_parents=True, filep=None):
- string = self.save_to_string(save_parents=save_parents)
- if filep:
- f = filep
- else:
- f = open(filename, 'w')
- f.write(string)
- f.close()
- self.filename=filename
-
- ##
- # Save the certificate to a random file in /tmp/
- # @param save_parents If save_parents==True, then also save the parent certificates.
- def save_to_random_tmp_file(self, save_parents=True):
- fp, filename = mkstemp(suffix='cert', text=True)
- fp = os.fdopen(fp, "w")
- self.save_to_file(filename, save_parents=True, filep=fp)
- return filename
-
- ##
- # Sets the issuer private key and name
- # @param key Keypair object containing the private key of the issuer
- # @param subject String containing the name of the issuer
- # @param cert (optional) Certificate object containing the name of the issuer
-
- def set_issuer(self, key, subject=None, cert=None):
- self.issuerKey = key
- if subject:
- # it's a mistake to use subject and cert params at the same time
- assert(not cert)
- if isinstance(subject, dict) or isinstance(subject, str):
- req = crypto.X509Req()
- reqSubject = req.get_subject()
- if (isinstance(subject, dict)):
- for key in reqSubject.keys():
- setattr(reqSubject, key, subject[key])
- else:
- setattr(reqSubject, "CN", subject)
- subject = reqSubject
- # subject is not valid once req is out of scope, so save req
- self.issuerReq = req
- if cert:
- # if a cert was supplied, then get the subject from the cert
- subject = cert.cert.get_subject()
- assert(subject)
- self.issuerSubject = subject
-
- ##
- # Get the issuer name
-
- def get_issuer(self, which="CN"):
- x = self.cert.get_issuer()
- return getattr(x, which)
-
- ##
- # Set the subject name of the certificate
-
- def set_subject(self, name):
- req = crypto.X509Req()
- subj = req.get_subject()
- if (isinstance(name, dict)):
- for key in name.keys():
- setattr(subj, key, name[key])
- else:
- setattr(subj, "CN", name)
- self.cert.set_subject(subj)
-
- ##
- # Get the subject name of the certificate
-
- def get_subject(self, which="CN"):
- x = self.cert.get_subject()
- return getattr(x, which)
-
- ##
- # Get a pretty-print subject name of the certificate
-
- def get_printable_subject(self):
- x = self.cert.get_subject()
- return "[ OU: %s, CN: %s, SubjectAltName: %s ]" % (getattr(x, "OU"), getattr(x, "CN"), self.get_data())
-
- ##
- # Get the public key of the certificate.
- #
- # @param key Keypair object containing the public key
-
- def set_pubkey(self, key):
- assert(isinstance(key, Keypair))
- self.cert.set_pubkey(key.get_openssl_pkey())
-
- ##
- # Get the public key of the certificate.
- # It is returned in the form of a Keypair object.
-
- def get_pubkey(self):
- m2x509 = X509.load_cert_string(self.save_to_string())
- pkey = Keypair()
- pkey.key = self.cert.get_pubkey()
- pkey.m2key = m2x509.get_pubkey()
- return pkey
-
- def set_intermediate_ca(self, val):
- return self.set_is_ca(val)
-
- # Set whether this cert is for a CA. All signers and only signers should be CAs.
- # The local member starts unset, letting us check that you only set it once
- # @param val Boolean indicating whether this cert is for a CA
- def set_is_ca(self, val):
- if val is None:
- return
-
- if self.isCA != None:
- # Can't double set properties
- raise Exception, "Cannot set basicConstraints CA:?? more than once. Was %s, trying to set as %s" % (self.isCA, val)
-
- self.isCA = val
- if val:
- self.add_extension('basicConstraints', 1, 'CA:TRUE')
- else:
- self.add_extension('basicConstraints', 1, 'CA:FALSE')
-
-
-
- ##
- # Add an X509 extension to the certificate. Add_extension can only be called
- # once for a particular extension name, due to limitations in the underlying
- # library.
- #
- # @param name string containing name of extension
- # @param value string containing value of the extension
-
- def add_extension(self, name, critical, value):
- oldExtVal = None
- try:
- oldExtVal = self.get_extension(name)
- except:
- # M2Crypto LookupError when the extension isn't there (yet)
- pass
-
- # This code limits you from adding the extension with the same value
- # The method comment says you shouldn't do this with the same name
- # But actually it (m2crypto) appears to allow you to do this.
- if oldExtVal and oldExtVal == value:
- # don't add this extension again
- # just do nothing as here
- return
- # FIXME: What if they are trying to set with a different value?
- # Is this ever OK? Or should we raise an exception?
-# elif oldExtVal:
-# raise "Cannot add extension %s which had val %s with new val %s" % (name, oldExtVal, value)
-
- ext = crypto.X509Extension (name, critical, value)
- self.cert.add_extensions([ext])
-
- ##
- # Get an X509 extension from the certificate
-
- def get_extension(self, name):
-
- # pyOpenSSL does not have a way to get extensions
- m2x509 = X509.load_cert_string(self.save_to_string())
- value = m2x509.get_ext(name).get_value()
-
- return value
-
- ##
- # Set_data is a wrapper around add_extension. It stores the parameter str in
- # the X509 subject_alt_name extension. Set_data can only be called once, due
- # to limitations in the underlying library.
-
- def set_data(self, str, field='subjectAltName'):
- # pyOpenSSL only allows us to add extensions, so if we try to set the
- # same extension more than once, it will not work
- if self.data.has_key(field):
- raise "Cannot set ", field, " more than once"
- self.data[field] = str
- self.add_extension(field, 0, str)
-
- ##
- # Return the data string that was previously set with set_data
-
- def get_data(self, field='subjectAltName'):
- if self.data.has_key(field):
- return self.data[field]
-
- try:
- uri = self.get_extension(field)
- self.data[field] = uri
- except LookupError:
- return None
-
- return self.data[field]
-
- ##
- # Sign the certificate using the issuer private key and issuer subject previous set with set_issuer().
-
- def sign(self):
- logger.debug('certificate.sign')
- assert self.cert != None
- assert self.issuerSubject != None
- assert self.issuerKey != None
- self.cert.set_issuer(self.issuerSubject)
- self.cert.sign(self.issuerKey.get_openssl_pkey(), self.digest)
-
- ##
- # Verify the authenticity of a certificate.
- # @param pkey is a Keypair object representing a public key. If Pkey
- # did not sign the certificate, then an exception will be thrown.
-
- def verify(self, pkey):
- # pyOpenSSL does not have a way to verify signatures
- m2x509 = X509.load_cert_string(self.save_to_string())
- m2pkey = pkey.get_m2_pkey()
- # verify it
- return m2x509.verify(m2pkey)
-
- # XXX alternatively, if openssl has been patched, do the much simpler:
- # try:
- # self.cert.verify(pkey.get_openssl_key())
- # return 1
- # except:
- # return 0
-
- ##
- # Return True if pkey is identical to the public key that is contained in the certificate.
- # @param pkey Keypair object
-
- def is_pubkey(self, pkey):
- return self.get_pubkey().is_same(pkey)
-
- ##
- # Given a certificate cert, verify that this certificate was signed by the
- # public key contained in cert. Throw an exception otherwise.
- #
- # @param cert certificate object
-
- def is_signed_by_cert(self, cert):
- k = cert.get_pubkey()
- result = self.verify(k)
- return result
-
- ##
- # Set the parent certficiate.
- #
- # @param p certificate object.
-
- def set_parent(self, p):
- self.parent = p
-
- ##
- # Return the certificate object of the parent of this certificate.
-
- def get_parent(self):
- return self.parent
-
- ##
- # Verification examines a chain of certificates to ensure that each parent
- # signs the child, and that some certificate in the chain is signed by a
- # trusted certificate.
- #
- # Verification is a basic recursion: <pre>
- # if this_certificate was signed by trusted_certs:
- # return
- # else
- # return verify_chain(parent, trusted_certs)
- # </pre>
- #
- # At each recursion, the parent is tested to ensure that it did sign the
- # child. If a parent did not sign a child, then an exception is thrown. If
- # the bottom of the recursion is reached and the certificate does not match
- # a trusted root, then an exception is thrown.
- # Also require that parents are CAs.
- #
- # @param Trusted_certs is a list of certificates that are trusted.
- #
-
- def verify_chain(self, trusted_certs = None):
- # Verify a chain of certificates. Each certificate must be signed by
- # the public key contained in it's parent. The chain is recursed
- # until a certificate is found that is signed by a trusted root.
-
- # verify expiration time
- if self.cert.has_expired():
- logger.debug("verify_chain: NO, Certificate %s has expired" % self.get_printable_subject())
- raise CertExpired(self.get_printable_subject(), "client cert")
-
- # if this cert is signed by a trusted_cert, then we are set
- for trusted_cert in trusted_certs:
- if self.is_signed_by_cert(trusted_cert):
- # verify expiration of trusted_cert ?
- if not trusted_cert.cert.has_expired():
- logger.debug("verify_chain: YES. Cert %s signed by trusted cert %s"%(
- self.get_printable_subject(), trusted_cert.get_printable_subject()))
- return trusted_cert
- else:
- logger.debug("verify_chain: NO. Cert %s is signed by trusted_cert %s, but that signer is expired..."%(
- self.get_printable_subject(),trusted_cert.get_printable_subject()))
- raise CertExpired(self.get_printable_subject()," signer trusted_cert %s"%trusted_cert.get_printable_subject())
-
- # if there is no parent, then no way to verify the chain
- if not self.parent:
- logger.debug("verify_chain: NO. %s has no parent and issuer %s is not in %d trusted roots"%(self.get_printable_subject(), self.get_issuer(), len(trusted_certs)))
- raise CertMissingParent(self.get_printable_subject() + ": Issuer %s not trusted by any of %d trusted roots, and cert has no parent." % (self.get_issuer(), len(trusted_certs)))
-
- # if it wasn't signed by the parent...
- if not self.is_signed_by_cert(self.parent):
- logger.debug("verify_chain: NO. %s is not signed by parent %s, but by %s"%\
- (self.get_printable_subject(),
- self.parent.get_printable_subject(),
- self.get_issuer()))
- raise CertNotSignedByParent("%s: Parent %s, issuer %s"\
- % (self.get_printable_subject(),
- self.parent.get_printable_subject(),
- self.get_issuer()))
-
- # Confirm that the parent is a CA. Only CAs can be trusted as
- # signers.
- # Note that trusted roots are not parents, so don't need to be
- # CAs.
- # Ugly - cert objects aren't parsed so we need to read the
- # extension and hope there are no other basicConstraints
- if not self.parent.isCA and not (self.parent.get_extension('basicConstraints') == 'CA:TRUE'):
- logger.warn("verify_chain: cert %s's parent %s is not a CA" % \
- (self.get_printable_subject(), self.parent.get_printable_subject()))
- raise CertNotSignedByParent("%s: Parent %s not a CA" % (self.get_printable_subject(),
- self.parent.get_printable_subject()))
-
- # if the parent isn't verified...
- logger.debug("verify_chain: .. %s, -> verifying parent %s"%\
- (self.get_printable_subject(),self.parent.get_printable_subject()))
- self.parent.verify_chain(trusted_certs)
-
- return
-
- ### more introspection
- def get_extensions(self):
- # pyOpenSSL does not have a way to get extensions
- triples=[]
- m2x509 = X509.load_cert_string(self.save_to_string())
- nb_extensions=m2x509.get_ext_count()
- logger.debug("X509 had %d extensions"%nb_extensions)
- for i in range(nb_extensions):
- ext=m2x509.get_ext_at(i)
- triples.append( (ext.get_name(), ext.get_value(), ext.get_critical(),) )
- return triples
-
- def get_data_names(self):
- return self.data.keys()
-
- def get_all_datas (self):
- triples=self.get_extensions()
- for name in self.get_data_names():
- triples.append( (name,self.get_data(name),'data',) )
- return triples
-
- # only informative
- def get_filename(self):
- return getattr(self,'filename',None)
-
- def dump (self, *args, **kwargs):
- print self.dump_string(*args, **kwargs)
-
- def dump_string (self,show_extensions=False):
- result = ""
- result += "CERTIFICATE for %s\n"%self.get_printable_subject()
- result += "Issued by %s\n"%self.get_issuer()
- filename=self.get_filename()
- if filename: result += "Filename %s\n"%filename
- if show_extensions:
- all_datas=self.get_all_datas()
- result += " has %d extensions/data attached"%len(all_datas)
- for (n,v,c) in all_datas:
- if c=='data':
- result += " data: %s=%s\n"%(n,v)
- else:
- result += " ext: %s (crit=%s)=<<<%s>>>\n"%(n,c,v)
- return result
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+\r
+##\r
+# SFA uses two crypto libraries: pyOpenSSL and M2Crypto to implement\r
+# the necessary crypto functionality. Ideally just one of these libraries\r
+# would be used, but unfortunately each of these libraries is independently\r
+# lacking. The pyOpenSSL library is missing many necessary functions, and\r
+# the M2Crypto library has crashed inside of some of the functions. The\r
+# design decision is to use pyOpenSSL whenever possible as it seems more\r
+# stable, and only use M2Crypto for those functions that are not possible\r
+# in pyOpenSSL.\r
+#\r
+# This module exports two classes: Keypair and Certificate.\r
+##\r
+#\r
+\r
+import functools\r
+import os\r
+import tempfile\r
+import base64\r
+from tempfile import mkstemp\r
+\r
+from OpenSSL import crypto\r
+import M2Crypto\r
+from M2Crypto import X509\r
+\r
+from sfa.util.faults import CertExpired, CertMissingParent, CertNotSignedByParent\r
+from sfa.util.sfalogging import logger\r
+\r
+glo_passphrase_callback = None\r
+\r
+##\r
+# A global callback may be implemented for requesting passphrases from the\r
+# user. The function will be called with three arguments:\r
+#\r
+# keypair_obj: the keypair object that is calling the passphrase\r
+# string: the string containing the private key that's being loaded\r
+# x: unknown, appears to be 0, comes from pyOpenSSL and/or m2crypto\r
+#\r
+# The callback should return a string containing the passphrase.\r
+\r
+def set_passphrase_callback(callback_func):\r
+ global glo_passphrase_callback\r
+\r
+ glo_passphrase_callback = callback_func\r
+\r
+##\r
+# Sets a fixed passphrase.\r
+\r
+def set_passphrase(passphrase):\r
+ set_passphrase_callback( lambda k,s,x: passphrase )\r
+\r
+##\r
+# Check to see if a passphrase works for a particular private key string.\r
+# Intended to be used by passphrase callbacks for input validation.\r
+\r
+def test_passphrase(string, passphrase):\r
+ try:\r
+ crypto.load_privatekey(crypto.FILETYPE_PEM, string, (lambda x: passphrase))\r
+ return True\r
+ except:\r
+ return False\r
+\r
+def convert_public_key(key):\r
+ keyconvert_path = "/usr/bin/keyconvert.py"\r
+ if not os.path.isfile(keyconvert_path):\r
+ raise IOError, "Could not find keyconvert in %s" % keyconvert_path\r
+\r
+ # we can only convert rsa keys\r
+ if "ssh-dss" in key:\r
+ raise Exception, "keyconvert: dss keys are not supported"\r
+\r
+ (ssh_f, ssh_fn) = tempfile.mkstemp()\r
+ ssl_fn = tempfile.mktemp()\r
+ os.write(ssh_f, key)\r
+ os.close(ssh_f)\r
+\r
+ cmd = keyconvert_path + " " + ssh_fn + " " + ssl_fn\r
+ os.system(cmd)\r
+\r
+ # this check leaves the temporary file containing the public key so\r
+ # that it can be expected to see why it failed.\r
+ # TODO: for production, cleanup the temporary files\r
+ if not os.path.exists(ssl_fn):\r
+ raise Exception, "keyconvert: generated certificate not found. keyconvert may have failed."\r
+\r
+ k = Keypair()\r
+ try:\r
+ k.load_pubkey_from_file(ssl_fn)\r
+ return k\r
+ except:\r
+ logger.log_exc("convert_public_key caught exception")\r
+ raise\r
+ finally:\r
+ # remove the temporary files\r
+ if os.path.exists(ssh_fn):\r
+ os.remove(ssh_fn)\r
+ if os.path.exists(ssl_fn):\r
+ os.remove(ssl_fn)\r
+\r
+##\r
+# Public-private key pairs are implemented by the Keypair class.\r
+# A Keypair object may represent both a public and private key pair, or it\r
+# may represent only a public key (this usage is consistent with OpenSSL).\r
+\r
+class Keypair:\r
+ key = None # public/private keypair\r
+ m2key = None # public key (m2crypto format)\r
+\r
+ ##\r
+ # Creates a Keypair object\r
+ # @param create If create==True, creates a new public/private key and\r
+ # stores it in the object\r
+ # @param string If string!=None, load the keypair from the string (PEM)\r
+ # @param filename If filename!=None, load the keypair from the file\r
+\r
+ def __init__(self, create=False, string=None, filename=None):\r
+ if create:\r
+ self.create()\r
+ if string:\r
+ self.load_from_string(string)\r
+ if filename:\r
+ self.load_from_file(filename)\r
+\r
+ ##\r
+ # Create a RSA public/private key pair and store it inside the keypair object\r
+\r
+ def create(self):\r
+ self.key = crypto.PKey()\r
+ self.key.generate_key(crypto.TYPE_RSA, 1024)\r
+\r
+ ##\r
+ # Save the private key to a file\r
+ # @param filename name of file to store the keypair in\r
+\r
+ def save_to_file(self, filename):\r
+ open(filename, 'w').write(self.as_pem())\r
+ self.filename=filename\r
+\r
+ ##\r
+ # Load the private key from a file. Implicity the private key includes the public key.\r
+\r
+ def load_from_file(self, filename):\r
+ self.filename=filename\r
+ buffer = open(filename, 'r').read()\r
+ self.load_from_string(buffer)\r
+\r
+ ##\r
+ # Load the private key from a string. Implicitly the private key includes the public key.\r
+\r
+ def load_from_string(self, string):\r
+ if glo_passphrase_callback:\r
+ self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string, functools.partial(glo_passphrase_callback, self, string) )\r
+ self.m2key = M2Crypto.EVP.load_key_string(string, functools.partial(glo_passphrase_callback, self, string) )\r
+ else:\r
+ self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string)\r
+ self.m2key = M2Crypto.EVP.load_key_string(string)\r
+\r
+ ##\r
+ # Load the public key from a string. No private key is loaded.\r
+\r
+ def load_pubkey_from_file(self, filename):\r
+ # load the m2 public key\r
+ m2rsakey = M2Crypto.RSA.load_pub_key(filename)\r
+ self.m2key = M2Crypto.EVP.PKey()\r
+ self.m2key.assign_rsa(m2rsakey)\r
+\r
+ # create an m2 x509 cert\r
+ m2name = M2Crypto.X509.X509_Name()\r
+ m2name.add_entry_by_txt(field="CN", type=0x1001, entry="junk", len=-1, loc=-1, set=0)\r
+ m2x509 = M2Crypto.X509.X509()\r
+ m2x509.set_pubkey(self.m2key)\r
+ m2x509.set_serial_number(0)\r
+ m2x509.set_issuer_name(m2name)\r
+ m2x509.set_subject_name(m2name)\r
+ ASN1 = M2Crypto.ASN1.ASN1_UTCTIME()\r
+ ASN1.set_time(500)\r
+ m2x509.set_not_before(ASN1)\r
+ m2x509.set_not_after(ASN1)\r
+ # x509v3 so it can have extensions\r
+ # prob not necc since this cert itself is junk but still...\r
+ m2x509.set_version(2)\r
+ junk_key = Keypair(create=True)\r
+ m2x509.sign(pkey=junk_key.get_m2_pkey(), md="sha1")\r
+\r
+ # convert the m2 x509 cert to a pyopenssl x509\r
+ m2pem = m2x509.as_pem()\r
+ pyx509 = crypto.load_certificate(crypto.FILETYPE_PEM, m2pem)\r
+\r
+ # get the pyopenssl pkey from the pyopenssl x509\r
+ self.key = pyx509.get_pubkey()\r
+ self.filename=filename\r
+\r
+ ##\r
+ # Load the public key from a string. No private key is loaded.\r
+\r
+ def load_pubkey_from_string(self, string):\r
+ (f, fn) = tempfile.mkstemp()\r
+ os.write(f, string)\r
+ os.close(f)\r
+ self.load_pubkey_from_file(fn)\r
+ os.remove(fn)\r
+\r
+ ##\r
+ # Return the private key in PEM format.\r
+\r
+ def as_pem(self):\r
+ return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.key)\r
+\r
+ ##\r
+ # Return an M2Crypto key object\r
+\r
+ def get_m2_pkey(self):\r
+ if not self.m2key:\r
+ self.m2key = M2Crypto.EVP.load_key_string(self.as_pem())\r
+ return self.m2key\r
+\r
+ ##\r
+ # Returns a string containing the public key represented by this object.\r
+\r
+ def get_pubkey_string(self):\r
+ m2pkey = self.get_m2_pkey()\r
+ return base64.b64encode(m2pkey.as_der())\r
+\r
+ ##\r
+ # Return an OpenSSL pkey object\r
+\r
+ def get_openssl_pkey(self):\r
+ return self.key\r
+\r
+ ##\r
+ # Given another Keypair object, return TRUE if the two keys are the same.\r
+\r
+ def is_same(self, pkey):\r
+ return self.as_pem() == pkey.as_pem()\r
+\r
+ def sign_string(self, data):\r
+ k = self.get_m2_pkey()\r
+ k.sign_init()\r
+ k.sign_update(data)\r
+ return base64.b64encode(k.sign_final())\r
+\r
+ def verify_string(self, data, sig):\r
+ k = self.get_m2_pkey()\r
+ k.verify_init()\r
+ k.verify_update(data)\r
+ return M2Crypto.m2.verify_final(k.ctx, base64.b64decode(sig), k.pkey)\r
+\r
+ def compute_hash(self, value):\r
+ return self.sign_string(str(value))\r
+\r
+ # only informative\r
+ def get_filename(self):\r
+ return getattr(self,'filename',None)\r
+\r
+ def dump (self, *args, **kwargs):\r
+ print self.dump_string(*args, **kwargs)\r
+\r
+ def dump_string (self):\r
+ result=""\r
+ result += "KEYPAIR: pubkey=%40s..."%self.get_pubkey_string()\r
+ filename=self.get_filename()\r
+ if filename: result += "Filename %s\n"%filename\r
+ return result\r
+\r
+##\r
+# The certificate class implements a general purpose X509 certificate, making\r
+# use of the appropriate pyOpenSSL or M2Crypto abstractions. It also adds\r
+# several addition features, such as the ability to maintain a chain of\r
+# parent certificates, and storage of application-specific data.\r
+#\r
+# Certificates include the ability to maintain a chain of parents. Each\r
+# certificate includes a pointer to it's parent certificate. When loaded\r
+# from a file or a string, the parent chain will be automatically loaded.\r
+# When saving a certificate to a file or a string, the caller can choose\r
+# whether to save the parent certificates as well.\r
+\r
+class Certificate:\r
+ digest = "md5"\r
+\r
+ cert = None\r
+ issuerKey = None\r
+ issuerSubject = None\r
+ parent = None\r
+ isCA = None # will be a boolean once set\r
+\r
+ separator="-----parent-----"\r
+\r
+ ##\r
+ # Create a certificate object.\r
+ #\r
+ # @param lifeDays life of cert in days - default is 1825==5 years\r
+ # @param create If create==True, then also create a blank X509 certificate.\r
+ # @param subject If subject!=None, then create a blank certificate and set\r
+ # it's subject name.\r
+ # @param string If string!=None, load the certficate from the string.\r
+ # @param filename If filename!=None, load the certficiate from the file.\r
+ # @param isCA If !=None, set whether this cert is for a CA\r
+\r
+ def __init__(self, lifeDays=1825, create=False, subject=None, string=None, filename=None, isCA=None):\r
+ self.data = {}\r
+ if create or subject:\r
+ self.create(lifeDays)\r
+ if subject:\r
+ self.set_subject(subject)\r
+ if string:\r
+ self.load_from_string(string)\r
+ if filename:\r
+ self.load_from_file(filename)\r
+\r
+ # Set the CA bit if a value was supplied\r
+ if isCA != None:\r
+ self.set_is_ca(isCA)\r
+\r
+ # Create a blank X509 certificate and store it in this object.\r
+\r
+ def create(self, lifeDays=1825):\r
+ self.cert = crypto.X509()\r
+ # FIXME: Use different serial #s\r
+ self.cert.set_serial_number(3)\r
+ self.cert.gmtime_adj_notBefore(0) # 0 means now\r
+ self.cert.gmtime_adj_notAfter(lifeDays*60*60*24) # five years is default\r
+ self.cert.set_version(2) # x509v3 so it can have extensions\r
+\r
+\r
+ ##\r
+ # Given a pyOpenSSL X509 object, store that object inside of this\r
+ # certificate object.\r
+\r
+ def load_from_pyopenssl_x509(self, x509):\r
+ self.cert = x509\r
+\r
+ ##\r
+ # Load the certificate from a string\r
+\r
+ def load_from_string(self, string):\r
+ # if it is a chain of multiple certs, then split off the first one and\r
+ # load it (support for the ---parent--- tag as well as normal chained certs)\r
+\r
+ string = string.strip()\r
+ \r
+ # If it's not in proper PEM format, wrap it\r
+ if string.count('-----BEGIN CERTIFICATE') == 0:\r
+ string = '-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----' % string\r
+\r
+ # If there is a PEM cert in there, but there is some other text first\r
+ # such as the text of the certificate, skip the text\r
+ beg = string.find('-----BEGIN CERTIFICATE')\r
+ if beg > 0:\r
+ # skipping over non cert beginning \r
+ string = string[beg:]\r
+\r
+ parts = []\r
+\r
+ if string.count('-----BEGIN CERTIFICATE-----') > 1 and \\r
+ string.count(Certificate.separator) == 0:\r
+ parts = string.split('-----END CERTIFICATE-----',1)\r
+ parts[0] += '-----END CERTIFICATE-----'\r
+ else:\r
+ parts = string.split(Certificate.separator, 1)\r
+\r
+ self.cert = crypto.load_certificate(crypto.FILETYPE_PEM, parts[0])\r
+\r
+ # if there are more certs, then create a parent and let the parent load\r
+ # itself from the remainder of the string\r
+ if len(parts) > 1 and parts[1] != '':\r
+ self.parent = self.__class__()\r
+ self.parent.load_from_string(parts[1])\r
+\r
+ ##\r
+ # Load the certificate from a file\r
+\r
+ def load_from_file(self, filename):\r
+ file = open(filename)\r
+ string = file.read()\r
+ self.load_from_string(string)\r
+ self.filename=filename\r
+\r
+ ##\r
+ # Save the certificate to a string.\r
+ #\r
+ # @param save_parents If save_parents==True, then also save the parent certificates.\r
+\r
+ def save_to_string(self, save_parents=True):\r
+ string = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)\r
+ if save_parents and self.parent:\r
+ string = string + self.parent.save_to_string(save_parents)\r
+ return string\r
+\r
+ ##\r
+ # Save the certificate to a file.\r
+ # @param save_parents If save_parents==True, then also save the parent certificates.\r
+\r
+ def save_to_file(self, filename, save_parents=True, filep=None):\r
+ string = self.save_to_string(save_parents=save_parents)\r
+ if filep:\r
+ f = filep\r
+ else:\r
+ f = open(filename, 'w')\r
+ f.write(string)\r
+ f.close()\r
+ self.filename=filename\r
+\r
+ ##\r
+ # Save the certificate to a random file in /tmp/\r
+ # @param save_parents If save_parents==True, then also save the parent certificates.\r
+ def save_to_random_tmp_file(self, save_parents=True):\r
+ fp, filename = mkstemp(suffix='cert', text=True)\r
+ fp = os.fdopen(fp, "w")\r
+ self.save_to_file(filename, save_parents=True, filep=fp)\r
+ return filename\r
+\r
+ ##\r
+ # Sets the issuer private key and name\r
+ # @param key Keypair object containing the private key of the issuer\r
+ # @param subject String containing the name of the issuer\r
+ # @param cert (optional) Certificate object containing the name of the issuer\r
+\r
+ def set_issuer(self, key, subject=None, cert=None):\r
+ self.issuerKey = key\r
+ if subject:\r
+ # it's a mistake to use subject and cert params at the same time\r
+ assert(not cert)\r
+ if isinstance(subject, dict) or isinstance(subject, str):\r
+ req = crypto.X509Req()\r
+ reqSubject = req.get_subject()\r
+ if (isinstance(subject, dict)):\r
+ for key in reqSubject.keys():\r
+ setattr(reqSubject, key, subject[key])\r
+ else:\r
+ setattr(reqSubject, "CN", subject)\r
+ subject = reqSubject\r
+ # subject is not valid once req is out of scope, so save req\r
+ self.issuerReq = req\r
+ if cert:\r
+ # if a cert was supplied, then get the subject from the cert\r
+ subject = cert.cert.get_subject()\r
+ assert(subject)\r
+ self.issuerSubject = subject\r
+\r
+ ##\r
+ # Get the issuer name\r
+\r
+ def get_issuer(self, which="CN"):\r
+ x = self.cert.get_issuer()\r
+ return getattr(x, which)\r
+\r
+ ##\r
+ # Set the subject name of the certificate\r
+\r
+ def set_subject(self, name):\r
+ req = crypto.X509Req()\r
+ subj = req.get_subject()\r
+ if (isinstance(name, dict)):\r
+ for key in name.keys():\r
+ setattr(subj, key, name[key])\r
+ else:\r
+ setattr(subj, "CN", name)\r
+ self.cert.set_subject(subj)\r
+\r
+ ##\r
+ # Get the subject name of the certificate\r
+\r
+ def get_subject(self, which="CN"):\r
+ x = self.cert.get_subject()\r
+ return getattr(x, which)\r
+\r
+ ##\r
+ # Get a pretty-print subject name of the certificate\r
+\r
+ def get_printable_subject(self):\r
+ x = self.cert.get_subject()\r
+ return "[ OU: %s, CN: %s, SubjectAltName: %s ]" % (getattr(x, "OU"), getattr(x, "CN"), self.get_data())\r
+\r
+ ##\r
+ # Get the public key of the certificate.\r
+ #\r
+ # @param key Keypair object containing the public key\r
+\r
+ def set_pubkey(self, key):\r
+ assert(isinstance(key, Keypair))\r
+ self.cert.set_pubkey(key.get_openssl_pkey())\r
+\r
+ ##\r
+ # Get the public key of the certificate.\r
+ # It is returned in the form of a Keypair object.\r
+\r
+ def get_pubkey(self):\r
+ m2x509 = X509.load_cert_string(self.save_to_string())\r
+ pkey = Keypair()\r
+ pkey.key = self.cert.get_pubkey()\r
+ pkey.m2key = m2x509.get_pubkey()\r
+ return pkey\r
+\r
+ def set_intermediate_ca(self, val):\r
+ return self.set_is_ca(val)\r
+\r
+ # Set whether this cert is for a CA. All signers and only signers should be CAs.\r
+ # The local member starts unset, letting us check that you only set it once\r
+ # @param val Boolean indicating whether this cert is for a CA\r
+ def set_is_ca(self, val):\r
+ if val is None:\r
+ return\r
+\r
+ if self.isCA != None:\r
+ # Can't double set properties\r
+ raise Exception, "Cannot set basicConstraints CA:?? more than once. Was %s, trying to set as %s" % (self.isCA, val)\r
+\r
+ self.isCA = val\r
+ if val:\r
+ self.add_extension('basicConstraints', 1, 'CA:TRUE')\r
+ else:\r
+ self.add_extension('basicConstraints', 1, 'CA:FALSE')\r
+\r
+\r
+\r
+ ##\r
+ # Add an X509 extension to the certificate. Add_extension can only be called\r
+ # once for a particular extension name, due to limitations in the underlying\r
+ # library.\r
+ #\r
+ # @param name string containing name of extension\r
+ # @param value string containing value of the extension\r
+\r
+ def add_extension(self, name, critical, value):\r
+ oldExtVal = None\r
+ try:\r
+ oldExtVal = self.get_extension(name)\r
+ except:\r
+ # M2Crypto LookupError when the extension isn't there (yet)\r
+ pass\r
+\r
+ # This code limits you from adding the extension with the same value\r
+ # The method comment says you shouldn't do this with the same name\r
+ # But actually it (m2crypto) appears to allow you to do this.\r
+ if oldExtVal and oldExtVal == value:\r
+ # don't add this extension again\r
+ # just do nothing as here\r
+ return\r
+ # FIXME: What if they are trying to set with a different value?\r
+ # Is this ever OK? Or should we raise an exception?\r
+# elif oldExtVal:\r
+# raise "Cannot add extension %s which had val %s with new val %s" % (name, oldExtVal, value)\r
+\r
+ ext = crypto.X509Extension (name, critical, value)\r
+ self.cert.add_extensions([ext])\r
+\r
+ ##\r
+ # Get an X509 extension from the certificate\r
+\r
+ def get_extension(self, name):\r
+\r
+ # pyOpenSSL does not have a way to get extensions\r
+ m2x509 = X509.load_cert_string(self.save_to_string())\r
+ value = m2x509.get_ext(name).get_value()\r
+\r
+ return value\r
+\r
+ ##\r
+ # Set_data is a wrapper around add_extension. It stores the parameter str in\r
+ # the X509 subject_alt_name extension. Set_data can only be called once, due\r
+ # to limitations in the underlying library.\r
+\r
+ def set_data(self, str, field='subjectAltName'):\r
+ # pyOpenSSL only allows us to add extensions, so if we try to set the\r
+ # same extension more than once, it will not work\r
+ if self.data.has_key(field):\r
+ raise "Cannot set ", field, " more than once"\r
+ self.data[field] = str\r
+ self.add_extension(field, 0, str)\r
+\r
+ ##\r
+ # Return the data string that was previously set with set_data\r
+\r
+ def get_data(self, field='subjectAltName'):\r
+ if self.data.has_key(field):\r
+ return self.data[field]\r
+\r
+ try:\r
+ uri = self.get_extension(field)\r
+ self.data[field] = uri\r
+ except LookupError:\r
+ return None\r
+\r
+ return self.data[field]\r
+\r
+ ##\r
+ # Sign the certificate using the issuer private key and issuer subject previous set with set_issuer().\r
+\r
+ def sign(self):\r
+ logger.debug('certificate.sign')\r
+ assert self.cert != None\r
+ assert self.issuerSubject != None\r
+ assert self.issuerKey != None\r
+ self.cert.set_issuer(self.issuerSubject)\r
+ self.cert.sign(self.issuerKey.get_openssl_pkey(), self.digest)\r
+\r
+ ##\r
+ # Verify the authenticity of a certificate.\r
+ # @param pkey is a Keypair object representing a public key. If Pkey\r
+ # did not sign the certificate, then an exception will be thrown.\r
+\r
+ def verify(self, pkey):\r
+ # pyOpenSSL does not have a way to verify signatures\r
+ m2x509 = X509.load_cert_string(self.save_to_string())\r
+ m2pkey = pkey.get_m2_pkey()\r
+ # verify it\r
+ return m2x509.verify(m2pkey)\r
+\r
+ # XXX alternatively, if openssl has been patched, do the much simpler:\r
+ # try:\r
+ # self.cert.verify(pkey.get_openssl_key())\r
+ # return 1\r
+ # except:\r
+ # return 0\r
+\r
+ ##\r
+ # Return True if pkey is identical to the public key that is contained in the certificate.\r
+ # @param pkey Keypair object\r
+\r
+ def is_pubkey(self, pkey):\r
+ return self.get_pubkey().is_same(pkey)\r
+\r
+ ##\r
+ # Given a certificate cert, verify that this certificate was signed by the\r
+ # public key contained in cert. Throw an exception otherwise.\r
+ #\r
+ # @param cert certificate object\r
+\r
+ def is_signed_by_cert(self, cert):\r
+ k = cert.get_pubkey()\r
+ result = self.verify(k)\r
+ return result\r
+\r
+ ##\r
+ # Set the parent certficiate.\r
+ #\r
+ # @param p certificate object.\r
+\r
+ def set_parent(self, p):\r
+ self.parent = p\r
+\r
+ ##\r
+ # Return the certificate object of the parent of this certificate.\r
+\r
+ def get_parent(self):\r
+ return self.parent\r
+\r
+ ##\r
+ # Verification examines a chain of certificates to ensure that each parent\r
+ # signs the child, and that some certificate in the chain is signed by a\r
+ # trusted certificate.\r
+ #\r
+ # Verification is a basic recursion: <pre>\r
+ # if this_certificate was signed by trusted_certs:\r
+ # return\r
+ # else\r
+ # return verify_chain(parent, trusted_certs)\r
+ # </pre>\r
+ #\r
+ # At each recursion, the parent is tested to ensure that it did sign the\r
+ # child. If a parent did not sign a child, then an exception is thrown. If\r
+ # the bottom of the recursion is reached and the certificate does not match\r
+ # a trusted root, then an exception is thrown.\r
+ # Also require that parents are CAs.\r
+ #\r
+ # @param Trusted_certs is a list of certificates that are trusted.\r
+ #\r
+\r
+ def verify_chain(self, trusted_certs = None):\r
+ # Verify a chain of certificates. Each certificate must be signed by\r
+ # the public key contained in it's parent. The chain is recursed\r
+ # until a certificate is found that is signed by a trusted root.\r
+\r
+ # verify expiration time\r
+ if self.cert.has_expired():\r
+ logger.debug("verify_chain: NO, Certificate %s has expired" % self.get_printable_subject())\r
+ raise CertExpired(self.get_printable_subject(), "client cert")\r
+\r
+ # if this cert is signed by a trusted_cert, then we are set\r
+ for trusted_cert in trusted_certs:\r
+ if self.is_signed_by_cert(trusted_cert):\r
+ # verify expiration of trusted_cert ?\r
+ if not trusted_cert.cert.has_expired():\r
+ logger.debug("verify_chain: YES. Cert %s signed by trusted cert %s"%(\r
+ self.get_printable_subject(), trusted_cert.get_printable_subject()))\r
+ return trusted_cert\r
+ else:\r
+ logger.debug("verify_chain: NO. Cert %s is signed by trusted_cert %s, but that signer is expired..."%(\r
+ self.get_printable_subject(),trusted_cert.get_printable_subject()))\r
+ raise CertExpired(self.get_printable_subject()," signer trusted_cert %s"%trusted_cert.get_printable_subject())\r
+\r
+ # if there is no parent, then no way to verify the chain\r
+ if not self.parent:\r
+ logger.debug("verify_chain: NO. %s has no parent and issuer %s is not in %d trusted roots"%(self.get_printable_subject(), self.get_issuer(), len(trusted_certs)))\r
+ raise CertMissingParent(self.get_printable_subject() + ": Issuer %s is not one of the %d trusted roots, and cert has no parent." % (self.get_issuer(), len(trusted_certs)))\r
+\r
+ # if it wasn't signed by the parent...\r
+ if not self.is_signed_by_cert(self.parent):\r
+ logger.debug("verify_chain: NO. %s is not signed by parent %s, but by %s"%\\r
+ (self.get_printable_subject(), \r
+ self.parent.get_printable_subject(), \r
+ self.get_issuer()))\r
+ raise CertNotSignedByParent("%s: Parent %s, issuer %s"\\r
+ % (self.get_printable_subject(), \r
+ self.parent.get_printable_subject(),\r
+ self.get_issuer()))\r
+\r
+ # Confirm that the parent is a CA. Only CAs can be trusted as\r
+ # signers.\r
+ # Note that trusted roots are not parents, so don't need to be\r
+ # CAs.\r
+ # Ugly - cert objects aren't parsed so we need to read the\r
+ # extension and hope there are no other basicConstraints\r
+ if not self.parent.isCA and not (self.parent.get_extension('basicConstraints') == 'CA:TRUE'):\r
+ logger.warn("verify_chain: cert %s's parent %s is not a CA" % \\r
+ (self.get_printable_subject(), self.parent.get_printable_subject()))\r
+ raise CertNotSignedByParent("%s: Parent %s not a CA" % (self.get_printable_subject(),\r
+ self.parent.get_printable_subject()))\r
+\r
+ # if the parent isn't verified...\r
+ logger.debug("verify_chain: .. %s, -> verifying parent %s"%\\r
+ (self.get_printable_subject(),self.parent.get_printable_subject()))\r
+ self.parent.verify_chain(trusted_certs)\r
+\r
+ return\r
+\r
+ ### more introspection\r
+ def get_extensions(self):\r
+ # pyOpenSSL does not have a way to get extensions\r
+ triples=[]\r
+ m2x509 = X509.load_cert_string(self.save_to_string())\r
+ nb_extensions=m2x509.get_ext_count()\r
+ logger.debug("X509 had %d extensions"%nb_extensions)\r
+ for i in range(nb_extensions):\r
+ ext=m2x509.get_ext_at(i)\r
+ triples.append( (ext.get_name(), ext.get_value(), ext.get_critical(),) )\r
+ return triples\r
+\r
+ def get_data_names(self):\r
+ return self.data.keys()\r
+\r
+ def get_all_datas (self):\r
+ triples=self.get_extensions()\r
+ for name in self.get_data_names():\r
+ triples.append( (name,self.get_data(name),'data',) )\r
+ return triples\r
+\r
+ # only informative\r
+ def get_filename(self):\r
+ return getattr(self,'filename',None)\r
+\r
+ def dump (self, *args, **kwargs):\r
+ print self.dump_string(*args, **kwargs)\r
+\r
+ def dump_string (self,show_extensions=False):\r
+ result = ""\r
+ result += "CERTIFICATE for %s\n"%self.get_printable_subject()\r
+ result += "Issued by %s\n"%self.get_issuer()\r
+ filename=self.get_filename()\r
+ if filename: result += "Filename %s\n"%filename\r
+ if show_extensions:\r
+ all_datas=self.get_all_datas()\r
+ result += " has %d extensions/data attached"%len(all_datas)\r
+ for (n,v,c) in all_datas:\r
+ if c=='data':\r
+ result += " data: %s=%s\n"%(n,v)\r
+ else:\r
+ result += " ext: %s (crit=%s)=<<<%s>>>\n"%(n,c,v)\r
+ return result\r
# Credentials are signed XML files that assign a subject gid privileges to an object gid
##
-import os,sys
+import os
from types import StringTypes
import datetime
from StringIO import StringIO
if not self.gidObject:
self.decode()
return self.gidObject
-
-
##
# Expiration: an absolute UTC time of expiration (as either an int or string or datetime)
if isinstance(privs, str):
self.privileges = Rights(string = privs)
else:
- self.privileges = privs
-
+ self.privileges = privs
##
# return the privileges as a Rights object
def updateRefID(self):
if not self.parent:
- self.set_refid('ref0')
+ self.set_refid('ref0')
return []
refs = []
next_cred = self.parent
-
while next_cred:
-
refs.append(next_cred.get_refid())
if next_cred.parent:
next_cred = next_cred.parent
# Verify the gids of this cred and of its parents
for cur_cred in self.get_credential_list():
cur_cred.get_gid_object().verify_chain(trusted_cert_objects)
- cur_cred.get_gid_caller().verify_chain(trusted_cert_objects)
+ cur_cred.get_gid_caller().verify_chain(trusted_cert_objects)
refs = []
refs.append("Sig_%s" % self.get_refid())
parentRefs = self.updateRefID()
for ref in parentRefs:
refs.append("Sig_%s" % ref)
+
for ref in refs:
# If caller explicitly passed in None that means skip xmlsec1 validation.
# Strange and not typical
msg = verified[mstart:mend]
raise CredentialNotVerifiable("xmlsec1 error verifying cred %s using Signature ID %s: %s %s" % (self.get_summary_tostring(), ref, msg, verified.strip()))
os.remove(filename)
-
+
# Verify the parents (delegation)
if self.parent:
self.verify_parent(self.parent)
+
# Make sure the issuer is the target's authority, and is
# itself a valid GID
self.verify_issuer(trusted_cert_objects)
-##
-# Implements SFA Credentials
-#
-# Credentials are layered on top of certificates, and are essentially a
-# certificate that stores a tuple of parameters.
-##
-
-import xmlrpclib
-
-from sfa.util.faults import MissingDelegateBit, ChildRightsNotSubsetOfParent
-from sfa.trust.certificate import Certificate
-from sfa.trust.gid import GID
-
-##
-# Credential is a tuple:
-# (GIDCaller, GIDObject, LifeTime, Privileges, Delegate)
-#
-# These fields are encoded using xmlrpc into the subjectAltName field of the
-# x509 certificate. Note: Call encode() once the fields have been filled in
-# to perform this encoding.
-
-class CredentialLegacy(Certificate):
- gidCaller = None
- gidObject = None
- lifeTime = None
- privileges = None
- delegate = False
-
- ##
- # Create a Credential object
- #
- # @param create If true, create a blank x509 certificate
- # @param subject If subject!=None, create an x509 cert with the subject name
- # @param string If string!=None, load the credential from the string
- # @param filename If filename!=None, load the credential from the file
-
- def __init__(self, create=False, subject=None, string=None, filename=None):
- Certificate.__init__(self, create, subject, string, filename)
-
- ##
- # set the GID of the caller
- #
- # @param gid GID object of the caller
-
- def set_gid_caller(self, gid):
- self.gidCaller = gid
- # gid origin caller is the caller's gid by default
- self.gidOriginCaller = gid
-
- ##
- # get the GID of the object
-
- def get_gid_caller(self):
- if not self.gidCaller:
- self.decode()
- return self.gidCaller
-
- ##
- # set the GID of the object
- #
- # @param gid GID object of the object
-
- def set_gid_object(self, gid):
- self.gidObject = gid
-
- ##
- # get the GID of the object
-
- def get_gid_object(self):
- if not self.gidObject:
- self.decode()
- return self.gidObject
-
- ##
- # set the lifetime of this credential
- #
- # @param lifetime lifetime of credential
-
- def set_lifetime(self, lifeTime):
- self.lifeTime = lifeTime
-
- ##
- # get the lifetime of the credential
-
- def get_lifetime(self):
- if not self.lifeTime:
- self.decode()
- return self.lifeTime
-
- ##
- # set the delegate bit
- #
- # @param delegate boolean (True or False)
-
- def set_delegate(self, delegate):
- self.delegate = delegate
-
- ##
- # get the delegate bit
-
- def get_delegate(self):
- if not self.delegate:
- self.decode()
- return self.delegate
-
- ##
- # set the privileges
- #
- # @param privs either a comma-separated list of privileges of a Rights object
-
- def set_privileges(self, privs):
- if isinstance(privs, str):
- self.privileges = Rights(string = privs)
- else:
- self.privileges = privs
-
- ##
- # return the privileges as a Rights object
-
- def get_privileges(self):
- if not self.privileges:
- self.decode()
- return self.privileges
-
- ##
- # determine whether the credential allows a particular operation to be
- # performed
- #
- # @param op_name string specifying name of operation ("lookup", "update", etc)
-
- def can_perform(self, op_name):
- rights = self.get_privileges()
- if not rights:
- return False
- return rights.can_perform(op_name)
-
- ##
- # Encode the attributes of the credential into a string and store that
- # string in the alt-subject-name field of the X509 object. This should be
- # done immediately before signing the credential.
-
- def encode(self):
- dict = {"gidCaller": None,
- "gidObject": None,
- "lifeTime": self.lifeTime,
- "privileges": None,
- "delegate": self.delegate}
- if self.gidCaller:
- dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True)
- if self.gidObject:
- dict["gidObject"] = self.gidObject.save_to_string(save_parents=True)
- if self.privileges:
- dict["privileges"] = self.privileges.save_to_string()
- str = xmlrpclib.dumps((dict,), allow_none=True)
- self.set_data('URI:http://' + str)
-
- ##
- # Retrieve the attributes of the credential from the alt-subject-name field
- # of the X509 certificate. This is automatically done by the various
- # get_* methods of this class and should not need to be called explicitly.
-
- def decode(self):
- data = self.get_data().lstrip('URI:http://')
-
- if data:
- dict = xmlrpclib.loads(data)[0][0]
- else:
- dict = {}
-
- self.lifeTime = dict.get("lifeTime", None)
- self.delegate = dict.get("delegate", None)
-
- privStr = dict.get("privileges", None)
- if privStr:
- self.privileges = Rights(string = privStr)
- else:
- self.privileges = None
-
- gidCallerStr = dict.get("gidCaller", None)
- if gidCallerStr:
- self.gidCaller = GID(string=gidCallerStr)
- else:
- self.gidCaller = None
-
- gidObjectStr = dict.get("gidObject", None)
- if gidObjectStr:
- self.gidObject = GID(string=gidObjectStr)
- else:
- self.gidObject = None
-
- ##
- # Verify that a chain of credentials is valid (see cert.py:verify). In
- # addition to the checks for ordinary certificates, verification also
- # ensures that the delegate bit was set by each parent in the chain. If
- # a delegate bit was not set, then an exception is thrown.
- #
- # Each credential must be a subset of the rights of the parent.
-
- def verify_chain(self, trusted_certs = None):
- # do the normal certificate verification stuff
- Certificate.verify_chain(self, trusted_certs)
-
- if self.parent:
- # make sure the parent delegated rights to the child
- if not self.parent.get_delegate():
- raise MissingDelegateBit(self.parent.get_subject())
-
- # make sure the rights given to the child are a subset of the
- # parents rights
- if not self.parent.get_privileges().is_superset(self.get_privileges()):
- raise ChildRightsNotSubsetOfParent(self.get_subject()
- + " " + self.parent.get_privileges().save_to_string()
- + " " + self.get_privileges().save_to_string())
-
- return
-
- ##
- # Dump the contents of a credential to stdout in human-readable format
- #
- # @param dump_parents If true, also dump the parent certificates
-
- def dump(self, *args, **kwargs):
- print self.dump_string(*args,**kwargs)
-
- def dump_string(self, dump_parents=False):
- result=""
- result += "CREDENTIAL %s\n" % self.get_subject()
-
- result += " privs: %s\n" % self.get_privileges().save_to_string()
-
- gidCaller = self.get_gid_caller()
- if gidCaller:
- result += " gidCaller:\n"
- gidCaller.dump(8, dump_parents)
-
- gidObject = self.get_gid_object()
- if gidObject:
- result += " gidObject:\n"
- result += gidObject.dump_string(8, dump_parents)
-
- result += " delegate: %s" % self.get_delegate()
-
- if self.parent and dump_parents:
- result += "PARENT\n"
- result += self.parent.dump_string(dump_parents)
-
- return result
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+##\r
+# Implements SFA Credentials\r
+#\r
+# Credentials are layered on top of certificates, and are essentially a\r
+# certificate that stores a tuple of parameters.\r
+##\r
+\r
+\r
+import xmlrpclib\r
+\r
+from sfa.util.faults import MissingDelegateBit, ChildRightsNotSubsetOfParent\r
+from sfa.trust.certificate import Certificate\r
+from sfa.trust.gid import GID\r
+\r
+##\r
+# Credential is a tuple:\r
+# (GIDCaller, GIDObject, LifeTime, Privileges, Delegate)\r
+#\r
+# These fields are encoded using xmlrpc into the subjectAltName field of the\r
+# x509 certificate. Note: Call encode() once the fields have been filled in\r
+# to perform this encoding.\r
+\r
+class CredentialLegacy(Certificate):\r
+ gidCaller = None\r
+ gidObject = None\r
+ lifeTime = None\r
+ privileges = None\r
+ delegate = False\r
+\r
+ ##\r
+ # Create a Credential object\r
+ #\r
+ # @param create If true, create a blank x509 certificate\r
+ # @param subject If subject!=None, create an x509 cert with the subject name\r
+ # @param string If string!=None, load the credential from the string\r
+ # @param filename If filename!=None, load the credential from the file\r
+\r
+ def __init__(self, create=False, subject=None, string=None, filename=None):\r
+ Certificate.__init__(self, create, subject, string, filename)\r
+\r
+ ##\r
+ # set the GID of the caller\r
+ #\r
+ # @param gid GID object of the caller\r
+\r
+ def set_gid_caller(self, gid):\r
+ self.gidCaller = gid\r
+ # gid origin caller is the caller's gid by default\r
+ self.gidOriginCaller = gid\r
+\r
+ ##\r
+ # get the GID of the object\r
+\r
+ def get_gid_caller(self):\r
+ if not self.gidCaller:\r
+ self.decode()\r
+ return self.gidCaller\r
+\r
+ ##\r
+ # set the GID of the object\r
+ #\r
+ # @param gid GID object of the object\r
+\r
+ def set_gid_object(self, gid):\r
+ self.gidObject = gid\r
+\r
+ ##\r
+ # get the GID of the object\r
+\r
+ def get_gid_object(self):\r
+ if not self.gidObject:\r
+ self.decode()\r
+ return self.gidObject\r
+\r
+ ##\r
+ # set the lifetime of this credential\r
+ #\r
+ # @param lifetime lifetime of credential\r
+\r
+ def set_lifetime(self, lifeTime):\r
+ self.lifeTime = lifeTime\r
+\r
+ ##\r
+ # get the lifetime of the credential\r
+\r
+ def get_lifetime(self):\r
+ if not self.lifeTime:\r
+ self.decode()\r
+ return self.lifeTime\r
+\r
+ ##\r
+ # set the delegate bit\r
+ #\r
+ # @param delegate boolean (True or False)\r
+\r
+ def set_delegate(self, delegate):\r
+ self.delegate = delegate\r
+\r
+ ##\r
+ # get the delegate bit\r
+\r
+ def get_delegate(self):\r
+ if not self.delegate:\r
+ self.decode()\r
+ return self.delegate\r
+\r
+ ##\r
+ # set the privileges\r
+ #\r
+ # @param privs either a comma-separated list of privileges of a Rights object\r
+\r
+ def set_privileges(self, privs):\r
+ if isinstance(privs, str):\r
+ self.privileges = Rights(string = privs)\r
+ else:\r
+ self.privileges = privs\r
+\r
+ ##\r
+ # return the privileges as a Rights object\r
+\r
+ def get_privileges(self):\r
+ if not self.privileges:\r
+ self.decode()\r
+ return self.privileges\r
+\r
+ ##\r
+ # determine whether the credential allows a particular operation to be\r
+ # performed\r
+ #\r
+ # @param op_name string specifying name of operation ("lookup", "update", etc)\r
+\r
+ def can_perform(self, op_name):\r
+ rights = self.get_privileges()\r
+ if not rights:\r
+ return False\r
+ return rights.can_perform(op_name)\r
+\r
+ ##\r
+ # Encode the attributes of the credential into a string and store that\r
+ # string in the alt-subject-name field of the X509 object. This should be\r
+ # done immediately before signing the credential.\r
+\r
+ def encode(self):\r
+ dict = {"gidCaller": None,\r
+ "gidObject": None,\r
+ "lifeTime": self.lifeTime,\r
+ "privileges": None,\r
+ "delegate": self.delegate}\r
+ if self.gidCaller:\r
+ dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True)\r
+ if self.gidObject:\r
+ dict["gidObject"] = self.gidObject.save_to_string(save_parents=True)\r
+ if self.privileges:\r
+ dict["privileges"] = self.privileges.save_to_string()\r
+ str = xmlrpclib.dumps((dict,), allow_none=True)\r
+ self.set_data('URI:http://' + str)\r
+\r
+ ##\r
+ # Retrieve the attributes of the credential from the alt-subject-name field\r
+ # of the X509 certificate. This is automatically done by the various\r
+ # get_* methods of this class and should not need to be called explicitly.\r
+\r
+ def decode(self):\r
+ data = self.get_data().lstrip('URI:http://')\r
+ \r
+ if data:\r
+ dict = xmlrpclib.loads(data)[0][0]\r
+ else:\r
+ dict = {}\r
+\r
+ self.lifeTime = dict.get("lifeTime", None)\r
+ self.delegate = dict.get("delegate", None)\r
+\r
+ privStr = dict.get("privileges", None)\r
+ if privStr:\r
+ self.privileges = Rights(string = privStr)\r
+ else:\r
+ self.privileges = None\r
+\r
+ gidCallerStr = dict.get("gidCaller", None)\r
+ if gidCallerStr:\r
+ self.gidCaller = GID(string=gidCallerStr)\r
+ else:\r
+ self.gidCaller = None\r
+\r
+ gidObjectStr = dict.get("gidObject", None)\r
+ if gidObjectStr:\r
+ self.gidObject = GID(string=gidObjectStr)\r
+ else:\r
+ self.gidObject = None\r
+\r
+ ##\r
+ # Verify that a chain of credentials is valid (see cert.py:verify). In\r
+ # addition to the checks for ordinary certificates, verification also\r
+ # ensures that the delegate bit was set by each parent in the chain. If\r
+ # a delegate bit was not set, then an exception is thrown.\r
+ #\r
+ # Each credential must be a subset of the rights of the parent.\r
+\r
+ def verify_chain(self, trusted_certs = None):\r
+ # do the normal certificate verification stuff\r
+ Certificate.verify_chain(self, trusted_certs)\r
+\r
+ if self.parent:\r
+ # make sure the parent delegated rights to the child\r
+ if not self.parent.get_delegate():\r
+ raise MissingDelegateBit(self.parent.get_subject())\r
+\r
+ # make sure the rights given to the child are a subset of the\r
+ # parents rights\r
+ if not self.parent.get_privileges().is_superset(self.get_privileges()):\r
+ raise ChildRightsNotSubsetOfParent(self.get_subject() \r
+ + " " + self.parent.get_privileges().save_to_string()\r
+ + " " + self.get_privileges().save_to_string())\r
+\r
+ return\r
+\r
+ ##\r
+ # Dump the contents of a credential to stdout in human-readable format\r
+ #\r
+ # @param dump_parents If true, also dump the parent certificates\r
+\r
+ def dump(self, *args, **kwargs):\r
+ print self.dump_string(*args,**kwargs)\r
+\r
+ def dump_string(self, dump_parents=False):\r
+ result=""\r
+ result += "CREDENTIAL %s\n" % self.get_subject()\r
+\r
+ result += " privs: %s\n" % self.get_privileges().save_to_string()\r
+\r
+ gidCaller = self.get_gid_caller()\r
+ if gidCaller:\r
+ result += " gidCaller:\n"\r
+ gidCaller.dump(8, dump_parents)\r
+\r
+ gidObject = self.get_gid_object()\r
+ if gidObject:\r
+ result += " gidObject:\n"\r
+ result += gidObject.dump_string(8, dump_parents)\r
+\r
+ result += " delegate: %s" % self.get_delegate()\r
+\r
+ if self.parent and dump_parents:\r
+ result += "PARENT\n"\r
+ result += self.parent.dump_string(dump_parents)\r
+\r
+ return result\r
-#----------------------------------------------------------------------
-# Copyright (c) 2008 Board of Trustees, Princeton University
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and/or hardware specification (the "Work") to
-# deal in the Work without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Work, and to permit persons to whom the Work
-# is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Work.
-#
-# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
-# IN THE WORK.
-#----------------------------------------------------------------------
-##
-# Implements SFA GID. GIDs are based on certificates, and the GID class is a
-# descendant of the certificate class.
-##
-
-import xmlrpclib
-import uuid
-
-from sfa.trust.certificate import Certificate
-
-from sfa.util.faults import GidInvalidParentHrn, GidParentHrn
-from sfa.util.sfalogging import logger
-from sfa.util.xrn import hrn_to_urn, urn_to_hrn, hrn_authfor_hrn
-
-##
-# Create a new uuid. Returns the UUID as a string.
-
-def create_uuid():
- return str(uuid.uuid4().int)
-
-##
-# GID is a tuple:
-# (uuid, urn, public_key)
-#
-# UUID is a unique identifier and is created by the python uuid module
-# (or the utility function create_uuid() in gid.py).
-#
-# HRN is a human readable name. It is a dotted form similar to a backward domain
-# name. For example, planetlab.us.arizona.bakers.
-#
-# URN is a human readable identifier of form:
-# "urn:publicid:IDN+toplevelauthority[:sub-auth.]*[\res. type]\ +object name"
-# For example, urn:publicid:IDN+planetlab:us:arizona+user+bakers
-#
-# PUBLIC_KEY is the public key of the principal identified by the UUID/HRN.
-# It is a Keypair object as defined in the cert.py module.
-#
-# It is expected that there is a one-to-one pairing between UUIDs and HRN,
-# but it is uncertain how this would be inforced or if it needs to be enforced.
-#
-# These fields are encoded using xmlrpc into the subjectAltName field of the
-# x509 certificate. Note: Call encode() once the fields have been filled in
-# to perform this encoding.
-
-
-class GID(Certificate):
- uuid = None
- hrn = None
- urn = None
- email = None # for adding to the SubjectAltName
-
- ##
- # Create a new GID object
- #
- # @param create If true, create the X509 certificate
- # @param subject If subject!=None, create the X509 cert and set the subject name
- # @param string If string!=None, load the GID from a string
- # @param filename If filename!=None, load the GID from a file
- # @param lifeDays life of GID in days - default is 1825==5 years
-
- def __init__(self, create=False, subject=None, string=None, filename=None, uuid=None, hrn=None, urn=None, lifeDays=1825):
-
- Certificate.__init__(self, lifeDays, create, subject, string, filename)
- if subject:
- logger.debug("Creating GID for subject: %s" % subject)
- if uuid:
- self.uuid = int(uuid)
- if hrn:
- self.hrn = hrn
- self.urn = hrn_to_urn(hrn, 'unknown')
- if urn:
- self.urn = urn
- self.hrn, type = urn_to_hrn(urn)
-
- def set_uuid(self, uuid):
- if isinstance(uuid, str):
- self.uuid = int(uuid)
- else:
- self.uuid = uuid
-
- def get_uuid(self):
- if not self.uuid:
- self.decode()
- return self.uuid
-
- def set_hrn(self, hrn):
- self.hrn = hrn
-
- def get_hrn(self):
- if not self.hrn:
- self.decode()
- return self.hrn
-
- def set_urn(self, urn):
- self.urn = urn
- self.hrn, type = urn_to_hrn(urn)
-
- def get_urn(self):
- if not self.urn:
- self.decode()
- return self.urn
-
- # Will be stuffed into subjectAltName
- def set_email(self, email):
- self.email = email
-
- def get_email(self):
- if not self.email:
- self.decode()
- return self.email
-
- def get_type(self):
- if not self.urn:
- self.decode()
- _, t = urn_to_hrn(self.urn)
- return t
-
- ##
- # Encode the GID fields and package them into the subject-alt-name field
- # of the X509 certificate. This must be called prior to signing the
- # certificate. It may only be called once per certificate.
-
- def encode(self):
- if self.urn:
- urn = self.urn
- else:
- urn = hrn_to_urn(self.hrn, None)
-
- str = "URI:" + urn
-
- if self.uuid:
- str += ", " + "URI:" + uuid.UUID(int=self.uuid).urn
-
- if self.email:
- str += ", " + "email:" + self.email
-
- self.set_data(str, 'subjectAltName')
-
-
-
-
- ##
- # Decode the subject-alt-name field of the X509 certificate into the
- # fields of the GID. This is automatically called by the various get_*()
- # functions in this class.
-
- def decode(self):
- data = self.get_data('subjectAltName')
- dict = {}
- if data:
- if data.lower().startswith('uri:http://<params>'):
- dict = xmlrpclib.loads(data[11:])[0][0]
- else:
- spl = data.split(', ')
- for val in spl:
- if val.lower().startswith('uri:urn:uuid:'):
- dict['uuid'] = uuid.UUID(val[4:]).int
- elif val.lower().startswith('uri:urn:publicid:idn+'):
- dict['urn'] = val[4:]
- elif val.lower().startswith('email:'):
- # FIXME: Ensure there isn't cruft in that address...
- # EG look for email:copy,....
- dict['email'] = val[6:]
-
- self.uuid = dict.get("uuid", None)
- self.urn = dict.get("urn", None)
- self.hrn = dict.get("hrn", None)
- self.email = dict.get("email", None)
- if self.urn:
- self.hrn = urn_to_hrn(self.urn)[0]
-
- ##
- # Dump the credential to stdout.
- #
- # @param indent specifies a number of spaces to indent the output
- # @param dump_parents If true, also dump the parents of the GID
-
- def dump(self, *args, **kwargs):
- print self.dump_string(*args,**kwargs)
-
- def dump_string(self, indent=0, dump_parents=False):
- result=" "*(indent-2) + "GID\n"
- result += " "*indent + "hrn:" + str(self.get_hrn()) +"\n"
- result += " "*indent + "urn:" + str(self.get_urn()) +"\n"
- result += " "*indent + "uuid:" + str(self.get_uuid()) + "\n"
- if self.get_email() is not None:
- result += " "*indent + "email:" + str(self.get_email()) + "\n"
- filename=self.get_filename()
- if filename: result += "Filename %s\n"%filename
-
- if self.parent and dump_parents:
- result += " "*indent + "parent:\n"
- result += self.parent.dump_string(indent+4, dump_parents)
- return result
-
- ##
- # Verify the chain of authenticity of the GID. First perform the checks
- # of the certificate class (verifying that each parent signs the child,
- # etc). In addition, GIDs also confirm that the parent's HRN is a prefix
- # of the child's HRN, and the parent is of type 'authority'.
- #
- # Verifying these prefixes prevents a rogue authority from signing a GID
- # for a principal that is not a member of that authority. For example,
- # planetlab.us.arizona cannot sign a GID for planetlab.us.princeton.foo.
-
- def verify_chain(self, trusted_certs = None):
- # do the normal certificate verification stuff
- trusted_root = Certificate.verify_chain(self, trusted_certs)
-
- if self.parent:
- # make sure the parent's hrn is a prefix of the child's hrn
- if not hrn_authfor_hrn(self.parent.get_hrn(), self.get_hrn()):
- raise GidParentHrn("This cert HRN %s isn't in the namespace for parent HRN %s" % (self.get_hrn(), self.parent.get_hrn()))
-
- # Parent must also be an authority (of some type) to sign a GID
- # There are multiple types of authority - accept them all here
- if not self.parent.get_type().find('authority') == 0:
- raise GidInvalidParentHrn("This cert %s's parent %s is not an authority (is a %s)" % (self.get_hrn(), self.parent.get_hrn(), self.parent.get_type()))
-
- # Then recurse up the chain - ensure the parent is a trusted
- # root or is in the namespace of a trusted root
- self.parent.verify_chain(trusted_certs)
- else:
- # make sure that the trusted root's hrn is a prefix of the child's
- trusted_gid = GID(string=trusted_root.save_to_string())
- trusted_type = trusted_gid.get_type()
- trusted_hrn = trusted_gid.get_hrn()
- #if trusted_type == 'authority':
- # trusted_hrn = trusted_hrn[:trusted_hrn.rindex('.')]
- cur_hrn = self.get_hrn()
- if not hrn_authfor_hrn(trusted_hrn, cur_hrn):
- raise GidParentHrn("Trusted root with HRN %s isn't a namespace authority for this cert %s" % (trusted_hrn, cur_hrn))
-
- # There are multiple types of authority - accept them all here
- if not trusted_type.find('authority') == 0:
- raise GidInvalidParentHrn("This cert %s's trusted root signer %s is not an authority (is a %s)" % (self.get_hrn(), trusted_hrn, trusted_type))
-
- return
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+##\r
+# Implements SFA GID. GIDs are based on certificates, and the GID class is a\r
+# descendant of the certificate class.\r
+##\r
+\r
+import xmlrpclib\r
+import uuid\r
+\r
+from sfa.trust.certificate import Certificate\r
+\r
+from sfa.util.faults import GidInvalidParentHrn, GidParentHrn\r
+from sfa.util.sfalogging import logger\r
+from sfa.util.xrn import hrn_to_urn, urn_to_hrn, hrn_authfor_hrn\r
+\r
+##\r
+# Create a new uuid. Returns the UUID as a string.\r
+\r
+def create_uuid():\r
+ return str(uuid.uuid4().int)\r
+\r
+##\r
+# GID is a tuple:\r
+# (uuid, urn, public_key)\r
+#\r
+# UUID is a unique identifier and is created by the python uuid module\r
+# (or the utility function create_uuid() in gid.py).\r
+#\r
+# HRN is a human readable name. It is a dotted form similar to a backward domain\r
+# name. For example, planetlab.us.arizona.bakers.\r
+#\r
+# URN is a human readable identifier of form:\r
+# "urn:publicid:IDN+toplevelauthority[:sub-auth.]*[\res. type]\ +object name"\r
+# For example, urn:publicid:IDN+planetlab:us:arizona+user+bakers \r
+#\r
+# PUBLIC_KEY is the public key of the principal identified by the UUID/HRN.\r
+# It is a Keypair object as defined in the cert.py module.\r
+#\r
+# It is expected that there is a one-to-one pairing between UUIDs and HRN,\r
+# but it is uncertain how this would be inforced or if it needs to be enforced.\r
+#\r
+# These fields are encoded using xmlrpc into the subjectAltName field of the\r
+# x509 certificate. Note: Call encode() once the fields have been filled in\r
+# to perform this encoding.\r
+\r
+\r
+class GID(Certificate):\r
+ uuid = None\r
+ hrn = None\r
+ urn = None\r
+ email = None # for adding to the SubjectAltName\r
+\r
+ ##\r
+ # Create a new GID object\r
+ #\r
+ # @param create If true, create the X509 certificate\r
+ # @param subject If subject!=None, create the X509 cert and set the subject name\r
+ # @param string If string!=None, load the GID from a string\r
+ # @param filename If filename!=None, load the GID from a file\r
+ # @param lifeDays life of GID in days - default is 1825==5 years\r
+\r
+ def __init__(self, create=False, subject=None, string=None, filename=None, uuid=None, hrn=None, urn=None, lifeDays=1825):\r
+ \r
+ Certificate.__init__(self, lifeDays, create, subject, string, filename)\r
+ if subject:\r
+ logger.debug("Creating GID for subject: %s" % subject)\r
+ if uuid:\r
+ self.uuid = int(uuid)\r
+ if hrn:\r
+ self.hrn = hrn\r
+ self.urn = hrn_to_urn(hrn, 'unknown')\r
+ if urn:\r
+ self.urn = urn\r
+ self.hrn, type = urn_to_hrn(urn)\r
+\r
+ def set_uuid(self, uuid):\r
+ if isinstance(uuid, str):\r
+ self.uuid = int(uuid)\r
+ else:\r
+ self.uuid = uuid\r
+\r
+ def get_uuid(self):\r
+ if not self.uuid:\r
+ self.decode()\r
+ return self.uuid\r
+\r
+ def set_hrn(self, hrn):\r
+ self.hrn = hrn\r
+\r
+ def get_hrn(self):\r
+ if not self.hrn:\r
+ self.decode()\r
+ return self.hrn\r
+\r
+ def set_urn(self, urn):\r
+ self.urn = urn\r
+ self.hrn, type = urn_to_hrn(urn)\r
+ \r
+ def get_urn(self):\r
+ if not self.urn:\r
+ self.decode()\r
+ return self.urn \r
+\r
+ # Will be stuffed into subjectAltName\r
+ def set_email(self, email):\r
+ self.email = email\r
+\r
+ def get_email(self):\r
+ if not self.email:\r
+ self.decode()\r
+ return self.email\r
+\r
+ def get_type(self):\r
+ if not self.urn:\r
+ self.decode()\r
+ _, t = urn_to_hrn(self.urn)\r
+ return t\r
+ \r
+ ##\r
+ # Encode the GID fields and package them into the subject-alt-name field\r
+ # of the X509 certificate. This must be called prior to signing the\r
+ # certificate. It may only be called once per certificate.\r
+\r
+ def encode(self):\r
+ if self.urn:\r
+ urn = self.urn\r
+ else:\r
+ urn = hrn_to_urn(self.hrn, None)\r
+ \r
+ str = "URI:" + urn\r
+\r
+ if self.uuid:\r
+ str += ", " + "URI:" + uuid.UUID(int=self.uuid).urn\r
+ \r
+ if self.email:\r
+ str += ", " + "email:" + self.email\r
+\r
+ self.set_data(str, 'subjectAltName')\r
+\r
+\r
+ ##\r
+ # Decode the subject-alt-name field of the X509 certificate into the\r
+ # fields of the GID. This is automatically called by the various get_*()\r
+ # functions in this class.\r
+\r
+ def decode(self):\r
+ data = self.get_data('subjectAltName')\r
+ dict = {}\r
+ if data:\r
+ if data.lower().startswith('uri:http://<params>'):\r
+ dict = xmlrpclib.loads(data[11:])[0][0]\r
+ else:\r
+ spl = data.split(', ')\r
+ for val in spl:\r
+ if val.lower().startswith('uri:urn:uuid:'):\r
+ dict['uuid'] = uuid.UUID(val[4:]).int\r
+ elif val.lower().startswith('uri:urn:publicid:idn+'):\r
+ dict['urn'] = val[4:]\r
+ elif val.lower().startswith('email:'):\r
+ # FIXME: Ensure there isn't cruft in that address...\r
+ # EG look for email:copy,....\r
+ dict['email'] = val[6:]\r
+ \r
+ self.uuid = dict.get("uuid", None)\r
+ self.urn = dict.get("urn", None)\r
+ self.hrn = dict.get("hrn", None)\r
+ self.email = dict.get("email", None)\r
+ if self.urn:\r
+ self.hrn = urn_to_hrn(self.urn)[0]\r
+\r
+ ##\r
+ # Dump the credential to stdout.\r
+ #\r
+ # @param indent specifies a number of spaces to indent the output\r
+ # @param dump_parents If true, also dump the parents of the GID\r
+\r
+ def dump(self, *args, **kwargs):\r
+ print self.dump_string(*args,**kwargs)\r
+\r
+ def dump_string(self, indent=0, dump_parents=False):\r
+ result=" "*(indent-2) + "GID\n"\r
+ result += " "*indent + "hrn:" + str(self.get_hrn()) +"\n"\r
+ result += " "*indent + "urn:" + str(self.get_urn()) +"\n"\r
+ result += " "*indent + "uuid:" + str(self.get_uuid()) + "\n"\r
+ if self.get_email() is not None:\r
+ result += " "*indent + "email:" + str(self.get_email()) + "\n"\r
+ filename=self.get_filename()\r
+ if filename: result += "Filename %s\n"%filename\r
+\r
+ if self.parent and dump_parents:\r
+ result += " "*indent + "parent:\n"\r
+ result += self.parent.dump_string(indent+4, dump_parents)\r
+ return result\r
+\r
+ ##\r
+ # Verify the chain of authenticity of the GID. First perform the checks\r
+ # of the certificate class (verifying that each parent signs the child,\r
+ # etc). In addition, GIDs also confirm that the parent's HRN is a prefix\r
+ # of the child's HRN, and the parent is of type 'authority'.\r
+ #\r
+ # Verifying these prefixes prevents a rogue authority from signing a GID\r
+ # for a principal that is not a member of that authority. For example,\r
+ # planetlab.us.arizona cannot sign a GID for planetlab.us.princeton.foo.\r
+\r
+ def verify_chain(self, trusted_certs = None):\r
+ # do the normal certificate verification stuff\r
+ trusted_root = Certificate.verify_chain(self, trusted_certs) \r
+ \r
+ if self.parent:\r
+ # make sure the parent's hrn is a prefix of the child's hrn\r
+ if not hrn_authfor_hrn(self.parent.get_hrn(), self.get_hrn()):\r
+ raise GidParentHrn("This cert HRN %s isn't in the namespace for parent HRN %s" % (self.get_hrn(), self.parent.get_hrn()))\r
+\r
+ # Parent must also be an authority (of some type) to sign a GID\r
+ # There are multiple types of authority - accept them all here\r
+ if not self.parent.get_type().find('authority') == 0:\r
+ raise GidInvalidParentHrn("This cert %s's parent %s is not an authority (is a %s)" % (self.get_hrn(), self.parent.get_hrn(), self.parent.get_type()))\r
+\r
+ # Then recurse up the chain - ensure the parent is a trusted\r
+ # root or is in the namespace of a trusted root\r
+ self.parent.verify_chain(trusted_certs)\r
+ else:\r
+ # make sure that the trusted root's hrn is a prefix of the child's\r
+ trusted_gid = GID(string=trusted_root.save_to_string())\r
+ trusted_type = trusted_gid.get_type()\r
+ trusted_hrn = trusted_gid.get_hrn()\r
+ #if trusted_type == 'authority':\r
+ # trusted_hrn = trusted_hrn[:trusted_hrn.rindex('.')]\r
+ cur_hrn = self.get_hrn()\r
+ if not hrn_authfor_hrn(trusted_hrn, cur_hrn):\r
+ raise GidParentHrn("Trusted root with HRN %s isn't a namespace authority for this cert: %s" % (trusted_hrn, cur_hrn))\r
+\r
+ # There are multiple types of authority - accept them all here\r
+ if not trusted_type.find('authority') == 0:\r
+ raise GidInvalidParentHrn("This cert %s's trusted root signer %s is not an authority (is a %s)" % (self.get_hrn(), trusted_hrn, trusted_type))\r
+\r
+ return\r
"""
Create top level records (includes root and sub authorities (local/remote)
"""
- if not hrn:
- hrn = self.config.SFA_INTERFACE_HRN
- # make sure parent exists
- parent_hrn = get_authority(hrn)
- if not parent_hrn:
- parent_hrn = hrn
- if not parent_hrn == hrn:
- self.create_top_level_auth(parent_hrn)
-
# create the authority if it doesnt alrady exist
if not self.auth_exists(hrn):
- self.create_auth(hrn)
+ self.create_auth(hrn, create_parents=True)
def get_interface_auth_info(self, create=True):
-##
-# SFA Configuration Info
-#
-# This module holds configuration parameters for SFA. There are two
-# main pieces of information that are used: the database connection and
-# the PLCAPI connection
-##
-
-##
-# SFA uses a MYSQL database to store records. This database may be
-# co-located with the PLC database, or it may be a separate database. The
-# following parameters define the connection to the database.
-#
-# Note that SFA does not access any of the PLC databases directly via
-# a mysql connection; All PLC databases are accessed via PLCAPI.
-
-import os.path
-import traceback
+#!/usr/bin/python
+import sys
+import os
+import time
+import ConfigParser
+import tempfile
+import codecs
+from StringIO import StringIO
+from sfa.util.xml import XML
+
+default_config = \
+"""
+"""
+
+def isbool(v):
+ return v.lower() in ("true", "false")
+
+def str2bool(v):
+ return v.lower() in ("true", "1")
class Config:
- """
- Parse the bash/Python/PHP version of the configuration file. Very
- fast but no type conversions.
- """
-
- def __init__(self, config_file = "/etc/sfa/sfa_config.py"):
- self.config_file = None
- self.config_path = None
- self.data_path = None
- self.load(config_file)
-
- def load(self, config_file):
+
+ def __init__(self, config_file='/etc/sfa/sfa_config'):
+ self._files = []
+ self.config_path = os.path.dirname(config_file)
+ self.config = ConfigParser.ConfigParser()
+ self.filename = config_file
+ if not os.path.isfile(self.filename):
+ self.create(self.filename)
+ self.load(self.filename)
+
+
+ def _header(self):
+ header = """
+DO NOT EDIT. This file was automatically generated at
+%s from:
+
+%s
+""" % (time.asctime(), os.linesep.join(self._files))
+
+ # Get rid of the surrounding newlines
+ return header.strip().split(os.linesep)
+
+ def create(self, filename):
+ if not os.path.exists(os.path.dirname(filename)):
+ os.makedirs(os.path.dirname(filename))
+ configfile = open(filename, 'w')
+ configfile.write(default_config)
+ configfile.close()
+
+
+ def load(self, filename):
+ if filename:
+ try:
+ self.config.read(filename)
+ except ConfigParser.MissingSectionHeaderError:
+ if filename.endswith('.xml'):
+ self.load_xml(filename)
+ else:
+ self.load_shell(filename)
+ self._files.append(filename)
+ self.set_attributes()
+
+ def load_xml(self, filename):
+ xml = XML(filename)
+ categories = xml.xpath('//configuration/variables/category')
+ for category in categories:
+ section_name = category.get('id')
+ if not self.config.has_section(section_name):
+ self.config.add_section(section_name)
+ options = category.xpath('./variablelist/variable')
+ for option in options:
+ option_name = option.get('id')
+ value = option.xpath('./value')[0].text
+ if not value:
+ value = ""
+ self.config.set(section_name, option_name, value)
+
+ def load_shell(self, filename):
+ f = open(filename, 'r')
+ for line in f:
+ try:
+ if line.startswith('#'):
+ continue
+ parts = line.strip().split("=")
+ if len(parts) < 2:
+ continue
+ option = parts[0]
+ value = parts[1].replace('"', '').replace("'","")
+ section, var = self.locate_varname(option, strict=False)
+ if section and var:
+ self.set(section, var, value)
+ except:
+ pass
+ f.close()
+
+ def locate_varname(self, varname, strict=True):
+ varname = varname.lower()
+ sections = self.config.sections()
+ section_name = ""
+ var_name = ""
+ for section in sections:
+ if varname.startswith(section.lower()) and len(section) > len(section_name):
+ section_name = section.lower()
+ var_name = varname.replace(section_name, "")[1:]
+ if strict and not self.config.has_option(section_name, var_name):
+ raise ConfigParser.NoOptionError(var_name, section_name)
+ return (section_name, var_name)
+
+ def set_attributes(self):
+ sections = self.config.sections()
+ for section in sections:
+ for item in self.config.items(section):
+ name = "%s_%s" % (section, item[0])
+ value = item[1]
+ if isbool(value):
+ value = str2bool(value)
+ elif value.isdigit():
+ value = int(value)
+ setattr(self, name, value)
+ setattr(self, name.upper(), value)
+
+
+ def verify(self, config1, config2, validate_method):
+ return True
+
+ def validate_type(self, var_type, value):
+ return True
+
+ @staticmethod
+ def is_xml(config_file):
+ try:
+ x = Xml(config_file)
+ return True
+ except:
+ return False
+
+ @staticmethod
+ def is_ini(config_file):
try:
- execfile(config_file, self.__dict__)
- self.config_file = config_file
- # path to configuration data
- self.config_path = os.path.dirname(config_file)
-
- ### xxx todo implement defaults in default_config.xml
- # path to server data
- if not hasattr(self, 'SFA_DATA_DIR'):
- # default to /var/lib/sfa not specified in config
- self.SFA_DATA_DIR="/var/lib/sfa"
- self.data_path = self.SFA_DATA_DIR
- else:
- self.data_path = self.SFA_DATA_DIR
+ c = ConfigParser.ConfigParser()
+ c.read(config_file)
+ return True
+ except ConfigParser.MissingSectionHeaderError:
+ return False
+
+
+ def dump(self, sections = []):
+ sys.stdout.write(output_python())
+
+ def output_python(self, encoding = "utf-8"):
+ buf = codecs.lookup(encoding)[3](StringIO())
+ buf.writelines(["# " + line + os.linesep for line in self._header()])
+
+ for section in self.sections():
+ buf.write("[%s]%s" % (section, os.linesep))
+ for (name,value) in self.items(section):
+ buf.write("%s=%s%s" % (name,value,os.linesep))
+ buf.write(os.linesep)
+ return buf.getvalue()
- # path to config data
- if not hasattr(self, 'SFA_CONFIG_DIR'):
- # default to /etc/sfa not specified in config
- self.SFA_CONFIG_DIR="/etc/sfa"
-
- if not hasattr(self, 'SFA_REGISTRY_LEVEL1_AUTH'):
- self.SFA_REGISTRY_LEVEL1_AUTH=None
-
- # create the data directory if it doesnt exist
- if not os.path.isdir(self.SFA_DATA_DIR):
- try:
- os.mkdir(self.SFA_DATA_DIR)
- except: pass
-
- except IOError, e:
- raise IOError, "Could not find or load the configuration file: %s" % config_file
+ def output_shell(self, show_comments = True, encoding = "utf-8"):
+ """
+ Return variables as a shell script.
+ """
+
+ buf = codecs.lookup(encoding)[3](StringIO())
+ buf.writelines(["# " + line + os.linesep for line in self._header()])
+
+ for section in self.sections():
+ for (name,value) in self.items(section):
+ # bash does not have the concept of NULL
+ if value:
+ option = "%s_%s" % (section.upper(), name.upper())
+ if isbool(value):
+ value = str(str2bool(value))
+ elif not value.isdigit():
+ value = '"%s"' % value
+ buf.write(option + "=" + value + os.linesep)
+ return buf.getvalue()
+
+ def output_php(selfi, encoding = "utf-8"):
+ """
+ Return variables as a PHP script.
+ """
+
+ buf = codecs.lookup(encoding)[3](StringIO())
+ buf.write("<?php" + os.linesep)
+ buf.writelines(["// " + line + os.linesep for line in self._header()])
+
+ for section in self.sections():
+ for (name,value) in self.items(section):
+ option = "%s_%s" % (section, name)
+ buf.write(os.linesep)
+ buf.write("// " + option + os.linesep)
+ if value is None:
+ value = 'NULL'
+ buf.write("define('%s', %s);" % (option, value) + os.linesep)
+
+ buf.write("?>" + os.linesep)
+
+ return buf.getvalue()
+
+ def output_xml(self, encoding = "utf-8"):
+ pass
+
+ def output_variables(self, encoding="utf-8"):
+ """
+ Return list of all variable names.
+ """
+
+ buf = codecs.lookup(encoding)[3](StringIO())
+ for section in self.sections():
+ for (name,value) in self.items(section):
+ option = "%s_%s" % (section,name)
+ buf.write(option + os.linesep)
+
+ return buf.getvalue()
+ pass
+
+ def write(self, filename=None):
+ if not filename:
+ filename = self.filename
+ configfile = open(filename, 'w')
+ self.config.write(configfile)
+
+ def save(self, filename=None):
+ self.write(filename)
+
def get_trustedroots_dir(self):
return self.config_path + os.sep + 'trusted_roots'
def get_openflow_aggrMgr_info(self):
aggr_mgr_ip = 'localhost'
- if (hasattr(self,'OPENFLOW_AGGREGATE_MANAGER_IP')):
+ if (hasattr(self,'openflow_aggregate_manager_ip')):
aggr_mgr_ip = self.OPENFLOW_AGGREGATE_MANAGER_IP
aggr_mgr_port = 2603
- if (hasattr(self,'OPENFLOW_AGGREGATE_MANAGER_PORT')):
+ if (hasattr(self,'openflow_aggregate_manager_port')):
aggr_mgr_port = self.OPENFLOW_AGGREGATE_MANAGER_PORT
return (aggr_mgr_ip,aggr_mgr_port)
def get_interface_hrn(self):
- if (hasattr(self,'SFA_INTERFACE_HRN')):
+ if (hasattr(self,'sfa_interface_hrn')):
return self.SFA_INTERFACE_HRN
else:
return "plc"
- # TODO: find a better place to put this method
- def get_max_aggrMgr_info(self):
- am_apiclient_path = '/usr/local/MAXGENI_AM_APIClient'
- if (hasattr(self,'MAXGENI_AM_APICLIENT_PATH')):
- am_client_path = self.MAXGENI_AM_APICLIENT_PATH
-
- am_url = 'https://geni.dragon.maxgigapop.net:8443/axis2/services/AggregateGENI'
- if (hasattr(self,'MAXGENI_AM_URL')):
- am_url = self.MAXGENI_AM_URL
-
- return (am_apiclient_path,am_url)
+ def __getattr__(self, attr):
+ return getattr(self.config, attr)
+if __name__ == '__main__':
+ filename = None
+ if len(sys.argv) > 1:
+ filename = sys.argv[1]
+ config = Config(filename)
+ else:
+ config = Config()
+ config.dump()
+
-
-class Enum(set):
- def __init__(self, *args, **kwds):
- set.__init__(self)
- enums = dict(zip(args, [object() for i in range(len(args))]), **kwds)
- for (key, value) in enums.items():
- setattr(self, key, value)
- self.add(eval('self.%s' % key))
-
-
-#def Enum2(*args, **kwds):
-# enums = dict(zip(sequential, range(len(sequential))), **named)
-# return type('Enum', (), enums)
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+\r
+class Enum(set):\r
+ def __init__(self, *args, **kwds):\r
+ set.__init__(self)\r
+ enums = dict(zip(args, [object() for i in range(len(args))]), **kwds)\r
+ for (key, value) in enums.items():\r
+ setattr(self, key, value)\r
+ self.add(eval('self.%s' % key))\r
+\r
+\r
+#def Enum2(*args, **kwds):\r
+# enums = dict(zip(sequential, range(len(sequential))), **named)\r
+# return type('Enum', (), enums)\r
-#
-# SFA API faults
-#
-
-import xmlrpclib
-from sfa.util.genicode import GENICODE
-
-class SfaFault(xmlrpclib.Fault):
- def __init__(self, faultCode, faultString, extra = None):
- if extra:
- faultString += ": " + str(extra)
- xmlrpclib.Fault.__init__(self, faultCode, faultString)
-
-class SfaInvalidAPIMethod(SfaFault):
- def __init__(self, method, interface = None, extra = None):
- faultString = "Invalid method " + method
- if interface:
- faultString += " for interface " + interface
- SfaFault.__init__(self, GENICODE.UNSUPPORTED, faultString, extra)
-
-class SfaInvalidArgumentCount(SfaFault):
- def __init__(self, got, min, max = min, extra = None):
- if min != max:
- expected = "%d-%d" % (min, max)
- else:
- expected = "%d" % min
- faultString = "Expected %s arguments, got %d" % \
- (expected, got)
- SfaFault.__init__(self, GENICODE.BADARGS, faultString, extra)
-
-class SfaInvalidArgument(SfaFault):
- def __init__(self, extra = None, name = None):
- if name is not None:
- faultString = "Invalid %s value" % name
- else:
- faultString = "Invalid argument"
- SfaFault.__init__(self, GENICODE.BADARGS, faultString, extra)
-
-class SfaAuthenticationFailure(SfaFault):
- def __init__(self, extra = None):
- faultString = "Failed to authenticate call"
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
-
-class SfaDBError(SfaFault):
- def __init__(self, extra = None):
- faultString = "Database error"
- SfaFault.__init__(self, GENICODE.DBERROR, faultString, extra)
-
-class SfaPermissionDenied(SfaFault):
- def __init__(self, extra = None):
- faultString = "Permission denied"
- SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)
-
-class SfaNotImplemented(SfaFault):
- def __init__(self, interface=None, extra = None):
- faultString = "Not implemented"
- if interface:
- faultString += " at interface " + interface
- SfaFault.__init__(self, GENICODE.UNSUPPORTED, faultString, extra)
-
-class SfaAPIError(SfaFault):
- def __init__(self, extra = None):
- faultString = "Internal API error"
- SfaFault.__init__(self, GENICODE.SERVERERROR, faultString, extra)
-
-class MalformedHrnException(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Malformed HRN: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, extra)
- def __str__(self):
- return repr(self.value)
-
-class TreeException(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Tree Exception: %(value)s, " % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class NonExistingRecord(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Non exsiting record %(value)s, " % locals()
- SfaFault.__init__(self, GENICODE.SEARCHFAILED, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class ExistingRecord(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Existing record: %(value)s, " % locals()
- SfaFault.__init__(self, GENICODE.REFUSED, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-
-class InvalidRPCParams(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Invalid RPC Params: %(value)s, " % locals()
- SfaFault.__init__(self, GENICODE.RPCERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-# SMBAKER exceptions follow
-
-class ConnectionKeyGIDMismatch(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Connection Key GID mismatch: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class MissingCallerGID(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Missing Caller GID: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class RecordNotFound(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Record not found: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class UnknownSfaType(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Unknown SFA Type: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class MissingAuthority(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Missing authority: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class PlanetLabRecordDoesNotExist(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "PlanetLab record does not exist : %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class PermissionError(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Permission error: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class InsufficientRights(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Insufficient rights: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class MissingDelegateBit(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Missing delegate bit: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class ChildRightsNotSubsetOfParent(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Child rights not subset of parent: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class CertMissingParent(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Cert missing parent: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class CertNotSignedByParent(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Cert not signed by parent: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class GidParentHrn(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Cert URN is not an extension of its parent: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class GidInvalidParentHrn(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "GID invalid parent hrn: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class SliverDoesNotExist(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Sliver does not exist : %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class BadRequestHash(xmlrpclib.Fault):
- def __init__(self, hash = None, extra = None):
- faultString = "bad request hash: " + str(hash)
- xmlrpclib.Fault.__init__(self, GENICODE.ERROR, faultString)
-
-class MissingTrustedRoots(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Trusted root directory does not exist: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.SERVERERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class MissingSfaInfo(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Missing information: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class InvalidRSpec(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Invalid RSpec: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class InvalidRSpecVersion(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Invalid RSpec version: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.BADVERSION, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class UnsupportedRSpecVersion(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Unsupported RSpec version: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.UNSUPPORTED, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class InvalidRSpecElement(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Invalid RSpec Element: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class InvalidXML(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Invalid XML Document: %(value)s" % locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class AccountNotEnabled(SfaFault):
- def __init__(self, extra = None):
- faultString = "Account Disabled"
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class CredentialNotVerifiable(SfaFault):
- def __init__(self, value, extra = None):
- self.value = value
- faultString = "Unable to verify credential: %(value)s, " %locals()
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
- def __str__(self):
- return repr(self.value)
-
-class CertExpired(SfaFault):
- def __init__(self, value, extra=None):
- self.value = value
- faultString = "%s cert is expired" % value
- SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)
-
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+#\r
+# SFA API faults\r
+#\r
+\r
+import xmlrpclib\r
+from sfa.util.genicode import GENICODE\r
+\r
+class SfaFault(xmlrpclib.Fault):\r
+ def __init__(self, faultCode, faultString, extra = None):\r
+ if extra:\r
+ faultString += ": " + str(extra)\r
+ xmlrpclib.Fault.__init__(self, faultCode, faultString)\r
+\r
+class SfaInvalidAPIMethod(SfaFault):\r
+ def __init__(self, method, interface = None, extra = None):\r
+ faultString = "Invalid method " + method\r
+ if interface:\r
+ faultString += " for interface " + interface\r
+ SfaFault.__init__(self, GENICODE.UNSUPPORTED, faultString, extra)\r
+\r
+class SfaInvalidArgumentCount(SfaFault):\r
+ def __init__(self, got, min, max = min, extra = None):\r
+ if min != max:\r
+ expected = "%d-%d" % (min, max)\r
+ else:\r
+ expected = "%d" % min\r
+ faultString = "Expected %s arguments, got %d" % \\r
+ (expected, got)\r
+ SfaFault.__init__(self, GENICODE.BADARGS, faultString, extra)\r
+\r
+class SfaInvalidArgument(SfaFault):\r
+ def __init__(self, extra = None, name = None):\r
+ if name is not None:\r
+ faultString = "Invalid %s value" % name\r
+ else:\r
+ faultString = "Invalid argument"\r
+ SfaFault.__init__(self, GENICODE.BADARGS, faultString, extra)\r
+\r
+class SfaAuthenticationFailure(SfaFault):\r
+ def __init__(self, extra = None):\r
+ faultString = "Failed to authenticate call"\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+\r
+class SfaDBError(SfaFault):\r
+ def __init__(self, extra = None):\r
+ faultString = "Database error"\r
+ SfaFault.__init__(self, GENICODE.DBERROR, faultString, extra)\r
+\r
+class SfaPermissionDenied(SfaFault):\r
+ def __init__(self, extra = None):\r
+ faultString = "Permission denied"\r
+ SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)\r
+\r
+class SfaNotImplemented(SfaFault):\r
+ def __init__(self, interface=None, extra = None):\r
+ faultString = "Not implemented"\r
+ if interface:\r
+ faultString += " at interface " + interface \r
+ SfaFault.__init__(self, GENICODE.UNSUPPORTED, faultString, extra)\r
+\r
+class SfaAPIError(SfaFault):\r
+ def __init__(self, extra = None):\r
+ faultString = "Internal API error"\r
+ SfaFault.__init__(self, GENICODE.SERVERERROR, faultString, extra)\r
+\r
+class MalformedHrnException(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Malformed HRN: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class TreeException(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Tree Exception: %(value)s, " % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class NonExistingRecord(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Non exsiting record %(value)s, " % locals()\r
+ SfaFault.__init__(self, GENICODE.SEARCHFAILED, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class ExistingRecord(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Existing record: %(value)s, " % locals()\r
+ SfaFault.__init__(self, GENICODE.REFUSED, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+ \r
+class InvalidRPCParams(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Invalid RPC Params: %(value)s, " % locals()\r
+ SfaFault.__init__(self, GENICODE.RPCERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+# SMBAKER exceptions follow\r
+\r
+class ConnectionKeyGIDMismatch(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Connection Key GID mismatch: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra) \r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class MissingCallerGID(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Missing Caller GID: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra) \r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class RecordNotFound(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Record not found: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class UnknownSfaType(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Unknown SFA Type: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class MissingAuthority(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Missing authority: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class PlanetLabRecordDoesNotExist(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "PlanetLab record does not exist : %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class PermissionError(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Permission error: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class InsufficientRights(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Insufficient rights: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class MissingDelegateBit(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Missing delegate bit: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class ChildRightsNotSubsetOfParent(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Child rights not subset of parent: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.FORBIDDEN, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class CertMissingParent(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Cert missing parent: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class CertNotSignedByParent(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Cert not signed by parent: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+ \r
+class GidParentHrn(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Cert URN is not an extension of its parent: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+ \r
+class GidInvalidParentHrn(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "GID invalid parent hrn: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class SliverDoesNotExist(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Sliver does not exist : %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class BadRequestHash(xmlrpclib.Fault):\r
+ def __init__(self, hash = None, extra = None):\r
+ faultString = "bad request hash: " + str(hash)\r
+ xmlrpclib.Fault.__init__(self, GENICODE.ERROR, faultString)\r
+\r
+class MissingTrustedRoots(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Trusted root directory does not exist: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.SERVERERROR, faultString, extra) \r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class MissingSfaInfo(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Missing information: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra) \r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class InvalidRSpec(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Invalid RSpec: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class InvalidRSpecVersion(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Invalid RSpec version: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.BADVERSION, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class UnsupportedRSpecVersion(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Unsupported RSpec version: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.UNSUPPORTED, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class InvalidRSpecElement(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Invalid RSpec Element: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class InvalidXML(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Invalid XML Document: %(value)s" % locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class AccountNotEnabled(SfaFault):\r
+ def __init__(self, extra = None):\r
+ faultString = "Account Disabled"\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class CredentialNotVerifiable(SfaFault):\r
+ def __init__(self, value, extra = None):\r
+ self.value = value\r
+ faultString = "Unable to verify credential: %(value)s, " %locals()\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ def __str__(self):\r
+ return repr(self.value)\r
+\r
+class CertExpired(SfaFault):\r
+ def __init__(self, value, extra=None):\r
+ self.value = value\r
+ faultString = "%s cert is expired" % value\r
+ SfaFault.__init__(self, GENICODE.ERROR, faultString, extra)\r
+ \r
-from sfa.util.enumeration import Enum
-
-GENICODE = Enum(
- SUCCESS=0,
- BADARGS=1,
- ERROR=2,
- FORBIDDEN=3,
- BADVERSION=4,
- SERVERERROR=5,
- TOOBIG=6,
- REFUSED=7,
- TIMEDOUT=8,
- DBERROR=9,
- RPCERROR=10,
- UNAVAILABLE=11,
- SEARCHFAILED=12,
- UNSUPPORTED=13,
- BUSY=14,
- EXPIRED=15,
- INPORGRESS=16,
- ALREADYEXISTS=17
-)
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+\r
+from sfa.util.enumeration import Enum\r
+\r
+GENICODE = Enum(\r
+ SUCCESS=0,\r
+ BADARGS=1,\r
+ ERROR=2,\r
+ FORBIDDEN=3,\r
+ BADVERSION=4,\r
+ SERVERERROR=5,\r
+ TOOBIG=6,\r
+ REFUSED=7,\r
+ TIMEDOUT=8,\r
+ DBERROR=9,\r
+ RPCERROR=10,\r
+ UNAVAILABLE=11,\r
+ SEARCHFAILED=12,\r
+ UNSUPPORTED=13,\r
+ BUSY=14,\r
+ EXPIRED=15,\r
+ INPORGRESS=16,\r
+ ALREADYEXISTS=17 \r
+) \r
-#!/usr/bin/python
-
-import os, sys
-import traceback
-import logging, logging.handlers
-
-CRITICAL=logging.CRITICAL
-ERROR=logging.ERROR
-WARNING=logging.WARNING
-INFO=logging.INFO
-DEBUG=logging.DEBUG
-
-# a logger that can handle tracebacks
-class _SfaLogger:
- def __init__ (self,logfile=None,loggername=None,level=logging.INFO):
- # default is to locate loggername from the logfile if avail.
- if not logfile:
- #loggername='console'
- #handler=logging.StreamHandler()
- #handler.setFormatter(logging.Formatter("%(levelname)s %(message)s"))
- logfile = "/var/log/sfa.log"
-
- if not loggername:
- loggername=os.path.basename(logfile)
- try:
- handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=5)
- except IOError:
- # This is usually a permissions error becaue the file is
- # owned by root, but httpd is trying to access it.
- tmplogfile=os.getenv("TMPDIR", "/tmp") + os.path.sep + os.path.basename(logfile)
- # In strange uses, 2 users on same machine might use same code,
- # meaning they would clobber each others files
- # We could (a) rename the tmplogfile, or (b)
- # just log to the console in that case.
- # Here we default to the console.
- if os.path.exists(tmplogfile) and not os.access(tmplogfile,os.W_OK):
- loggername = loggername + "-console"
- handler = logging.StreamHandler()
- else:
- handler=logging.handlers.RotatingFileHandler(tmplogfile,maxBytes=1000000, backupCount=5)
- handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
- self.logger=logging.getLogger(loggername)
- self.logger.setLevel(level)
- # check if logger already has the handler we're about to add
- handler_exists = False
- for l_handler in self.logger.handlers:
- if l_handler.baseFilename == handler.baseFilename and \
- l_handler.level == handler.level:
- handler_exists = True
-
- if not handler_exists:
- self.logger.addHandler(handler)
-
- self.loggername=loggername
-
- def setLevel(self,level):
- self.logger.setLevel(level)
-
- # shorthand to avoid having to import logging all over the place
- def setLevelDebug(self):
- self.logger.setLevel(logging.DEBUG)
-
- # define a verbose option with s/t like
- # parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0)
- # and pass the coresponding options.verbose to this method to adjust level
- def setLevelFromOptVerbose(self,verbose):
- if verbose==0:
- self.logger.setLevel(logging.WARNING)
- elif verbose==1:
- self.logger.setLevel(logging.INFO)
- elif verbose>=2:
- self.logger.setLevel(logging.DEBUG)
- # in case some other code needs a boolean
- def getBoolVerboseFromOpt(self,verbose):
- return verbose>=1
-
- ####################
- def info(self, msg):
- self.logger.info(msg)
-
- def debug(self, msg):
- self.logger.debug(msg)
-
- def warn(self, msg):
- self.logger.warn(msg)
-
- # some code is using logger.warn(), some is using logger.warning()
- def warning(self, msg):
- self.logger.warning(msg)
-
- def error(self, msg):
- self.logger.error(msg)
-
- def critical(self, msg):
- self.logger.critical(msg)
-
- # logs an exception - use in an except statement
- def log_exc(self,message):
- self.error("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))
- self.error("%s END TRACEBACK"%message)
-
- def log_exc_critical(self,message):
- self.critical("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))
- self.critical("%s END TRACEBACK"%message)
-
- # for investigation purposes, can be placed anywhere
- def log_stack(self,message):
- to_log="".join(traceback.format_stack())
- self.info("%s BEG STACK"%message+"\n"+to_log)
- self.info("%s END STACK"%message)
-
- def enable_console(self, stream=sys.stdout):
- formatter = logging.Formatter("%(message)s")
- handler = logging.StreamHandler(stream)
- handler.setFormatter(formatter)
- self.logger.addHandler(handler)
-
-
-info_logger = _SfaLogger(loggername='info', level=logging.INFO)
-debug_logger = _SfaLogger(loggername='debug', level=logging.DEBUG)
-warn_logger = _SfaLogger(loggername='warning', level=logging.WARNING)
-error_logger = _SfaLogger(loggername='error', level=logging.ERROR)
-critical_logger = _SfaLogger(loggername='critical', level=logging.CRITICAL)
-logger = info_logger
-sfi_logger = _SfaLogger(logfile=os.path.expanduser("~/.sfi/")+'sfi.log',loggername='sfilog', level=logging.DEBUG)
-########################################
-import time
-
-def profile(logger):
- """
- Prints the runtime of the specified callable. Use as a decorator, e.g.,
-
- @profile(logger)
- def foo(...):
- ...
- """
- def logger_profile(callable):
- def wrapper(*args, **kwds):
- start = time.time()
- result = callable(*args, **kwds)
- end = time.time()
- args = map(str, args)
- args += ["%s = %s" % (name, str(value)) for (name, value) in kwds.iteritems()]
- # should probably use debug, but then debug is not always enabled
- logger.info("PROFILED %s (%s): %.02f s" % (callable.__name__, ", ".join(args), end - start))
- return result
- return wrapper
- return logger_profile
-
-
-if __name__ == '__main__':
- print 'testing sfalogging into logger.log'
- logger1=_SfaLogger('logger.log', loggername='std(info)')
- logger2=_SfaLogger('logger.log', loggername='error', level=logging.ERROR)
- logger3=_SfaLogger('logger.log', loggername='debug', level=logging.DEBUG)
-
- for (logger,msg) in [ (logger1,"std(info)"),(logger2,"error"),(logger3,"debug")]:
-
- print "====================",msg, logger.logger.handlers
-
- logger.enable_console()
- logger.critical("logger.critical")
- logger.error("logger.error")
- logger.warn("logger.warning")
- logger.info("logger.info")
- logger.debug("logger.debug")
- logger.setLevel(logging.DEBUG)
- logger.debug("logger.debug again")
-
- @profile(logger)
- def sleep(seconds = 1):
- time.sleep(seconds)
-
- logger.info('console.info')
- sleep(0.5)
- logger.setLevel(logging.DEBUG)
- sleep(0.25)
-
+#!/usr/bin/python\r
+\r
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+\r
+import os, sys\r
+import traceback\r
+import logging, logging.handlers\r
+\r
+CRITICAL=logging.CRITICAL\r
+ERROR=logging.ERROR\r
+WARNING=logging.WARNING\r
+INFO=logging.INFO\r
+DEBUG=logging.DEBUG\r
+\r
+# a logger that can handle tracebacks \r
+class _SfaLogger:\r
+ def __init__ (self,logfile=None,loggername=None,level=logging.INFO):\r
+ # default is to locate loggername from the logfile if avail.\r
+ if not logfile:\r
+ #loggername='console'\r
+ #handler=logging.StreamHandler()\r
+ #handler.setFormatter(logging.Formatter("%(levelname)s %(message)s"))\r
+ logfile = "/var/log/sfa.log"\r
+\r
+ if not loggername:\r
+ loggername=os.path.basename(logfile)\r
+ try:\r
+ handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=5) \r
+ except IOError:\r
+ # This is usually a permissions error becaue the file is\r
+ # owned by root, but httpd is trying to access it.\r
+ tmplogfile=os.getenv("TMPDIR", "/tmp") + os.path.sep + os.path.basename(logfile)\r
+ # In strange uses, 2 users on same machine might use same code,\r
+ # meaning they would clobber each others files\r
+ # We could (a) rename the tmplogfile, or (b)\r
+ # just log to the console in that case.\r
+ # Here we default to the console.\r
+ if os.path.exists(tmplogfile) and not os.access(tmplogfile,os.W_OK):\r
+ loggername = loggername + "-console"\r
+ handler = logging.StreamHandler()\r
+ else:\r
+ handler=logging.handlers.RotatingFileHandler(tmplogfile,maxBytes=1000000, backupCount=5) \r
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))\r
+ self.logger=logging.getLogger(loggername)\r
+ self.logger.setLevel(level)\r
+ # check if logger already has the handler we're about to add\r
+ handler_exists = False\r
+ for l_handler in self.logger.handlers:\r
+ if l_handler.baseFilename == handler.baseFilename and \\r
+ l_handler.level == handler.level:\r
+ handler_exists = True \r
+\r
+ if not handler_exists:\r
+ self.logger.addHandler(handler)\r
+\r
+ self.loggername=loggername\r
+\r
+ def setLevel(self,level):\r
+ self.logger.setLevel(level)\r
+\r
+ # shorthand to avoid having to import logging all over the place\r
+ def setLevelDebug(self):\r
+ self.logger.setLevel(logging.DEBUG)\r
+\r
+ # define a verbose option with s/t like\r
+ # parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0)\r
+ # and pass the coresponding options.verbose to this method to adjust level\r
+ def setLevelFromOptVerbose(self,verbose):\r
+ if verbose==0:\r
+ self.logger.setLevel(logging.WARNING)\r
+ elif verbose==1:\r
+ self.logger.setLevel(logging.INFO)\r
+ elif verbose>=2:\r
+ self.logger.setLevel(logging.DEBUG)\r
+ # in case some other code needs a boolean\r
+ def getBoolVerboseFromOpt(self,verbose):\r
+ return verbose>=1\r
+\r
+ ####################\r
+ def info(self, msg):\r
+ self.logger.info(msg)\r
+\r
+ def debug(self, msg):\r
+ self.logger.debug(msg)\r
+ \r
+ def warn(self, msg):\r
+ self.logger.warn(msg)\r
+\r
+ # some code is using logger.warn(), some is using logger.warning()\r
+ def warning(self, msg):\r
+ self.logger.warning(msg)\r
+ \r
+ def error(self, msg):\r
+ self.logger.error(msg) \r
+ \r
+ def critical(self, msg):\r
+ self.logger.critical(msg)\r
+\r
+ # logs an exception - use in an except statement\r
+ def log_exc(self,message):\r
+ self.error("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))\r
+ self.error("%s END TRACEBACK"%message)\r
+ \r
+ def log_exc_critical(self,message):\r
+ self.critical("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))\r
+ self.critical("%s END TRACEBACK"%message)\r
+ \r
+ # for investigation purposes, can be placed anywhere\r
+ def log_stack(self,message):\r
+ to_log="".join(traceback.format_stack())\r
+ self.info("%s BEG STACK"%message+"\n"+to_log)\r
+ self.info("%s END STACK"%message)\r
+\r
+ def enable_console(self, stream=sys.stdout):\r
+ formatter = logging.Formatter("%(message)s")\r
+ handler = logging.StreamHandler(stream)\r
+ handler.setFormatter(formatter)\r
+ self.logger.addHandler(handler)\r
+\r
+\r
+info_logger = _SfaLogger(loggername='info', level=logging.INFO)\r
+debug_logger = _SfaLogger(loggername='debug', level=logging.DEBUG)\r
+warn_logger = _SfaLogger(loggername='warning', level=logging.WARNING)\r
+error_logger = _SfaLogger(loggername='error', level=logging.ERROR)\r
+critical_logger = _SfaLogger(loggername='critical', level=logging.CRITICAL)\r
+logger = info_logger\r
+sfi_logger = _SfaLogger(logfile=os.path.expanduser("~/.sfi/")+'sfi.log',loggername='sfilog', level=logging.DEBUG)\r
+########################################\r
+import time\r
+\r
+def profile(logger):\r
+ """\r
+ Prints the runtime of the specified callable. Use as a decorator, e.g.,\r
+ \r
+ @profile(logger)\r
+ def foo(...):\r
+ ...\r
+ """\r
+ def logger_profile(callable):\r
+ def wrapper(*args, **kwds):\r
+ start = time.time()\r
+ result = callable(*args, **kwds)\r
+ end = time.time()\r
+ args = map(str, args)\r
+ args += ["%s = %s" % (name, str(value)) for (name, value) in kwds.iteritems()]\r
+ # should probably use debug, but then debug is not always enabled\r
+ logger.info("PROFILED %s (%s): %.02f s" % (callable.__name__, ", ".join(args), end - start))\r
+ return result\r
+ return wrapper\r
+ return logger_profile\r
+\r
+\r
+if __name__ == '__main__': \r
+ print 'testing sfalogging into logger.log'\r
+ logger1=_SfaLogger('logger.log', loggername='std(info)')\r
+ logger2=_SfaLogger('logger.log', loggername='error', level=logging.ERROR)\r
+ logger3=_SfaLogger('logger.log', loggername='debug', level=logging.DEBUG)\r
+ \r
+ for (logger,msg) in [ (logger1,"std(info)"),(logger2,"error"),(logger3,"debug")]:\r
+ \r
+ print "====================",msg, logger.logger.handlers\r
+ \r
+ logger.enable_console()\r
+ logger.critical("logger.critical")\r
+ logger.error("logger.error")\r
+ logger.warn("logger.warning")\r
+ logger.info("logger.info")\r
+ logger.debug("logger.debug")\r
+ logger.setLevel(logging.DEBUG)\r
+ logger.debug("logger.debug again")\r
+ \r
+ @profile(logger)\r
+ def sleep(seconds = 1):\r
+ time.sleep(seconds)\r
+\r
+ logger.info('console.info')\r
+ sleep(0.5)\r
+ logger.setLevel(logging.DEBUG)\r
+ sleep(0.25)\r
+\r
-from types import StringTypes
-import dateutil.parser
-import datetime
-import time
-
-from sfa.util.sfalogging import logger
-
-DATEFORMAT = "%Y-%m-%dT%H:%M:%SZ"
-
-def utcparse(input):
- """ Translate a string into a time using dateutil.parser.parse but make sure it's in UTC time and strip
-the timezone, so that it's compatible with normal datetime.datetime objects.
-
-For safety this can also handle inputs that are either timestamps, or datetimes
-"""
- # perpare the input for the checks below by
- # casting strings ('1327098335') to ints
- if isinstance(input, StringTypes):
- try:
- input = int(input)
- except ValueError:
- pass
-
- if isinstance (input, datetime.datetime):
- logger.warn ("argument to utcparse already a datetime - doing nothing")
- return input
- elif isinstance (input, StringTypes):
- t = dateutil.parser.parse(input)
- if t.utcoffset() is not None:
- t = t.utcoffset() + t.replace(tzinfo=None)
- return t
- elif isinstance (input, (int,float,long)):
- return datetime.datetime.fromtimestamp(input)
- else:
- logger.error("Unexpected type in utcparse [%s]"%type(input))
-
-def datetime_to_string(input):
- return datetime.datetime.strftime(input, DATEFORMAT)
-
-def datetime_to_utc(input):
- return time.gmtime(datetime_to_epoch(input))
-
-def datetime_to_epoch(input):
- return int(time.mktime(input.timetuple()))
-
-
-
+#----------------------------------------------------------------------\r
+# Copyright (c) 2008 Board of Trustees, Princeton University\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining\r
+# a copy of this software and/or hardware specification (the "Work") to\r
+# deal in the Work without restriction, including without limitation the\r
+# rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+# and/or sell copies of the Work, and to permit persons to whom the Work\r
+# is furnished to do so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be\r
+# included in all copies or substantial portions of the Work.\r
+#\r
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS\r
+# IN THE WORK.\r
+#----------------------------------------------------------------------\r
+from types import StringTypes\r
+import dateutil.parser\r
+import datetime\r
+import time\r
+\r
+from sfa.util.sfalogging import logger\r
+\r
+DATEFORMAT = "%Y-%m-%dT%H:%M:%SZ"\r
+\r
+def utcparse(input):\r
+ """ Translate a string into a time using dateutil.parser.parse but make sure it's in UTC time and strip\r
+the timezone, so that it's compatible with normal datetime.datetime objects.\r
+\r
+For safety this can also handle inputs that are either timestamps, or datetimes\r
+"""\r
+ # prepare the input for the checks below by\r
+ # casting strings ('1327098335') to ints\r
+ if isinstance(input, StringTypes):\r
+ try:\r
+ input = int(input)\r
+ except ValueError:\r
+ pass\r
+\r
+ if isinstance (input, datetime.datetime):\r
+ logger.warn ("argument to utcparse already a datetime - doing nothing")\r
+ return input\r
+ elif isinstance (input, StringTypes):\r
+ t = dateutil.parser.parse(input)\r
+ if t.utcoffset() is not None:\r
+ t = t.utcoffset() + t.replace(tzinfo=None)\r
+ return t\r
+ elif isinstance (input, (int,float,long)):\r
+ return datetime.datetime.fromtimestamp(input)\r
+ else:\r
+ logger.error("Unexpected type in utcparse [%s]"%type(input))\r
+\r
+def datetime_to_string(input):\r
+ return datetime.datetime.strftime(input, DATEFORMAT)\r
+\r
+def datetime_to_utc(input):\r
+ return time.gmtime(datetime_to_epoch(input))\r
+\r
+def datetime_to_epoch(input):\r
+ return int(time.mktime(input.timetuple()))\r
namespaces = self.namespaces
elems = self.element.xpath(xpath, namespaces=namespaces)
return [XmlElement(elem, namespaces) for elem in elems]
-
+
def add_element(self, tagname, **kwds):
element = etree.SubElement(self.element, tagname, **kwds)
return XmlElement(element, self.namespaces)
if not element:
element = self.root
element.remove_attribute(name)
-
+
def add_element(self, *args, **kwds):
"""
Wrapper around etree.SubElement(). Adds an element to
#----------------------------------------------------------------------
import re
-import sys
+
from sfa.util.faults import SfaAPIError
# for convenience and smoother translation - we should get rid of these functions eventually
# A better alternative than childHRN.startswith(parentHRN)
# e.g. hrn_is_auth_for_hrn('a\.b', 'a\.b.c.d') -> True,
# but hrn_is_auth_for_hrn('a', 'a\.b.c.d') -> False
- # Also hrn_is_uauth_for_hrn('a\.b.c.d', 'a\.b.c.d') -> True
+ # Also hrn_is_auth_for_hrn('a\.b.c.d', 'a\.b.c.d') -> True
@staticmethod
def hrn_is_auth_for_hrn(parenthrn, hrn):
if parenthrn == hrn:
# provide either urn, or (hrn + type)
def __init__ (self, xrn, type=None):
if not xrn: xrn = ""
-
# user has specified xrn : guess if urn or hrn
if Xrn.is_urn(xrn):
self.hrn=None
self.hrn=xrn
self.type=type
self.hrn_to_urn()
+ self._normalize()
# happens all the time ..
# if not type:
# debug_logger.debug("type-less Xrn's are not safe")
# self.authority keeps a list
if not hasattr(self,'authority'):
self.authority=Xrn.hrn_auth_list(self.hrn)
-
-
+
def get_leaf(self):
self._normalize()
return self.leaf
def get_authority_urn(self):
self._normalize()
return ':'.join( [Xrn.unescape(x) for x in self.authority] )
-
- def get_sliver_id(self, slice_id, node_id, index=0, authority=None):
+
+ def get_sliver_id(self, slice_id, node_id=None, index=0, authority=None):
self._normalize()
urn = self.get_urn()
if authority:
authority_hrn = self.get_authority_hrn()
if not authority_hrn.startswith(authority):
- hrn = ".".join([authority,self.get_authority_hrn(), self.get_leaf()])
+ hrn = ".".join([authority,authority_hrn, self.get_leaf()])
else:
- hrn = ".".join([self.get_authority_hrn(), self.get_leaf()])
+ hrn = ".".join([authority_hrn, self.get_leaf()])
urn = Xrn(hrn, self.get_type()).get_urn()
- return ":".join(map(str, [urn, slice_id, node_id, index]))
-
+ parts = [part for part in [urn, slice_id, node_id, index] if part is not None]
+ return ":".join(map(str, [parts]))
+
def urn_to_hrn(self):
"""
compute tuple (hrn, type) from urn
if self.type and self.type.startswith('authority'):
self.authority = Xrn.hrn_auth_list(self.hrn)
- leaf = self.get_leaf()
+ leaf = self.get_leaf()
#if not self.authority:
# self.authority = [self.hrn]
type_parts = self.type.split("+")
name = 'sa'
if len(type_parts) > 1:
name = type_parts[1]
- auth_parts = [part for part in [self.get_authority_urn(), leaf] if part]
+ auth_parts = [part for part in [self.get_authority_urn(), leaf] if part]
authority_string = ":".join(auth_parts)
else:
self.authority = Xrn.hrn_auth_list(self.hrn)