Merge branch 'master' into senslab2
authorSandrine Avakian <sandrine.avakian@inria.fr>
Thu, 30 Aug 2012 12:23:36 +0000 (14:23 +0200)
committerSandrine Avakian <sandrine.avakian@inria.fr>
Thu, 30 Aug 2012 12:23:36 +0000 (14:23 +0200)
Conflicts:
sfa/trust/credential.py
sfa/util/xrn.py

36 files changed:
config/default_config.xml
config/sfa-config [new file with mode: 0755]
config/sfa-config-tty
init.d/sfa
setup.py
sfa.spec
sfa/client/sfaadmin.py
sfa/client/sfaclientlib.py
sfa/client/sfi.py
sfa/importer/plimporter.py
sfa/openstack/client.py
sfa/openstack/nova_driver.py
sfa/openstack/osaggregate.py
sfa/openstack/osxrn.py
sfa/openstack/security_group.py
sfa/openstack/shell.py
sfa/planetlab/plaggregate.py
sfa/planetlab/pldriver.py
sfa/rspecs/elements/versions/sfav1Sliver.py
sfa/rspecs/versions/pgv2.py
sfa/server/sfaapi.py
sfa/server/sfaserver.py
sfa/server/threadedserver.py
sfa/trust/certificate.py
sfa/trust/credential.py
sfa/trust/credential_legacy.py
sfa/trust/gid.py
sfa/trust/hierarchy.py
sfa/util/config.py
sfa/util/enumeration.py
sfa/util/faults.py
sfa/util/genicode.py
sfa/util/sfalogging.py
sfa/util/sfatime.py
sfa/util/xml.py
sfa/util/xrn.py

index 5093193..6d80712 100644 (file)
@@ -62,6 +62,13 @@ Thierry Parmentelat
             </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>
 
diff --git a/config/sfa-config b/config/sfa-config
new file mode 100755 (executable)
index 0000000..29aa179
--- /dev/null
@@ -0,0 +1,168 @@
+#!/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()
index 4008cd1..0b708e2 100755 (executable)
@@ -1,8 +1,17 @@
-#!/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
@@ -34,7 +43,489 @@ configuration={ \
     '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)
index 136e47c..6a3ed37 100755 (executable)
 
 # 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
@@ -82,8 +80,8 @@ function reload () {
     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
@@ -96,11 +94,15 @@ function reload () {
     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
@@ -115,7 +117,7 @@ function reload () {
     #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
 
 }
 
@@ -123,7 +125,7 @@ function reload () {
 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
 
@@ -190,7 +192,7 @@ function db_start () {
        ######## 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
 
@@ -202,8 +204,8 @@ function db_start () {
        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
 
@@ -231,6 +233,7 @@ function db_start () {
        check
     fi
     check
+    sfaadmin reg sync_db
 
     MESSAGE=$"SFA: Checking for PostgreSQL server"
     echo -n "$MESSAGE"
@@ -241,7 +244,7 @@ function db_start () {
 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
@@ -263,11 +266,11 @@ function start() {
     # 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
index 96cb8cb..69a9e3c 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -12,6 +12,7 @@ from distutils.core import setup
 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', 
index 229379b..5ea2d8c 100644 (file)
--- a/sfa.spec
+++ b/sfa.spec
@@ -25,7 +25,6 @@ Summary: the SFA python libraries
 Group: Applications/System
 BuildRequires: make
 
-Requires: myplc-config
 Requires: python >= 2.5
 Requires: pyOpenSSL >= 0.7
 Requires: m2crypto
@@ -168,6 +167,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_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
index c95f550..7640e93 100755 (executable)
@@ -177,6 +177,12 @@ class RegistryCommands(Commands):
         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)
index 521a220..c5c5b80 100644 (file)
@@ -10,6 +10,7 @@
 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 
@@ -132,7 +133,12 @@ class SfaClientBootstrap:
         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
@@ -238,7 +244,7 @@ class SfaClientBootstrap:
 
     # 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):
index a9725f4..e837270 100644 (file)
@@ -13,10 +13,12 @@ import datetime
 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
@@ -530,16 +532,32 @@ class Sfi:
     ####################
     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
@@ -557,7 +575,7 @@ class Sfi:
         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
@@ -566,7 +584,7 @@ class Sfi:
         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
@@ -619,7 +637,7 @@ class Sfi:
             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)
index 1e04e54..4b5c7c1 100644 (file)
@@ -167,9 +167,13 @@ class PlImporter:
         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'])
@@ -332,6 +336,7 @@ class PlImporter:
     
             # 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']:
index f0d717e..215d333 100644 (file)
@@ -23,14 +23,26 @@ def parse_novarc(filename):
 
 
 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) 
@@ -50,10 +62,19 @@ class GlanceClient:
         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'),
@@ -63,6 +84,9 @@ class NovaClient:
                                          service_type='compute',
                                          service_name='',  
                                          )
+
+    def connect(self, *args, **kwds):
+        self.__init__(*args, **kwds)
                               
     def __getattr__(self, name):
         return getattr(self.client, name)        
index 638811e..d1d2c1a 100644 (file)
@@ -43,7 +43,7 @@ class NovaDriver(Driver):
 
     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:
@@ -90,7 +90,9 @@ class NovaDriver(Driver):
         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:
@@ -339,15 +341,18 @@ class NovaDriver(Driver):
                 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):
@@ -383,10 +388,13 @@ class NovaDriver(Driver):
         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") 
         
@@ -395,23 +403,25 @@ class NovaDriver(Driver):
         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:
@@ -426,9 +436,7 @@ class NovaDriver(Driver):
     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.
@@ -441,17 +449,22 @@ class NovaDriver(Driver):
         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)
     
@@ -462,9 +475,10 @@ class NovaDriver(Driver):
         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")
index 602cfe8..d5d0bfa 100644 (file)
@@ -21,6 +21,7 @@ from sfa.planetlab.plxrn import PlXrn
 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):
@@ -84,28 +85,38 @@ class OSAggregate:
         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]
@@ -113,8 +124,19 @@ class OSAggregate:
             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)) 
@@ -122,11 +144,9 @@ class OSAggregate:
                 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'], 
@@ -134,7 +154,7 @@ class OSAggregate:
                 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()
@@ -167,8 +187,20 @@ class OSAggregate:
         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)
@@ -179,7 +211,7 @@ class OSAggregate:
                 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       
         
 
@@ -212,11 +244,24 @@ class OSAggregate:
 
  
 
-    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)
@@ -227,43 +272,61 @@ class OSAggregate:
             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)
index 0a888b7..e7d1d21 100644 (file)
@@ -48,8 +48,7 @@ class OSXrn(Xrn):
 
     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
         
         
index 32f86fb..70e191e 100644 (file)
@@ -8,13 +8,15 @@ class SecurityGroup:
         
     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")
 
@@ -54,13 +56,8 @@ class SecurityGroup:
                 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)
 
@@ -78,7 +75,7 @@ class SecurityGroup:
                 '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)
index 7fab67f..acb9cff 100644 (file)
@@ -31,9 +31,9 @@ class Shell:
             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')
index d232cc5..46154e6 100644 (file)
@@ -152,7 +152,7 @@ class PlAggregate:
             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})
@@ -298,7 +298,7 @@ class PlAggregate:
             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
index d72f2e6..0951e8c 100644 (file)
@@ -615,6 +615,10 @@ class PlDriver (Driver):
         # 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:
@@ -658,7 +662,7 @@ class PlDriver (Driver):
             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'],
index 94cecd1..851d11c 100644 (file)
@@ -21,9 +21,6 @@ class SFAv1Sliver:
             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):
index 9ac4d37..f96b449 100644 (file)
@@ -173,6 +173,14 @@ class PGv2(RSpecVersion):
     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):
index 2510ec4..9fe7656 100644 (file)
@@ -37,7 +37,7 @@ class SfaApi (XmlrpcApi):
     """
 
     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):
         
index f392b78..b1e8d33 100644 (file)
@@ -36,7 +36,7 @@ class SfaServer(threading.Thread):
         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()
index 1a78f35..7bc434c 100644 (file)
@@ -73,6 +73,10 @@ def verify_callback(conn, x509, err, depth, preverify):
        #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
@@ -143,7 +147,7 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
 
         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
index 595812b..35435cd 100644 (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
index 09835d9..9ccf18f 100644 (file)
@@ -26,7 +26,7 @@
 # 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
@@ -364,8 +364,6 @@ class Credential(object):
         if not self.gidObject:
             self.decode()
         return self.gidObject
-
-
             
     ##
     # Expiration: an absolute UTC time of expiration (as either an int or string or datetime)
@@ -404,8 +402,7 @@ class Credential(object):
         if isinstance(privs, str):
             self.privileges = Rights(string = privs)
         else:
-            self.privileges = privs
-        
+            self.privileges = privs        
 
     ##
     # return the privileges as a Rights object
@@ -589,15 +586,13 @@ class Credential(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
@@ -836,7 +831,7 @@ class Credential(object):
             # 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())
@@ -844,6 +839,7 @@ class Credential(object):
         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
@@ -864,10 +860,11 @@ class Credential(object):
                     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)
index e66e699..85ddc68 100644 (file)
-##
-# 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
index 8d1017f..2653cf0 100644 (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.
-#----------------------------------------------------------------------
-##
-# 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
index 2040510..cf330db 100644 (file)
@@ -174,18 +174,9 @@ class Hierarchy:
         """
         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):
index eaf2aea..444ceeb 100644 (file)
-##
-# 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()
+    
index 4e508bd..bea2ce9 100644 (file)
@@ -1,13 +1,35 @@
-
-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
index ef277ff..848d818 100644 (file)
-#
-# 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
index 855d440..61ae489 100644 (file)
@@ -1,22 +1,45 @@
-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
index 2e4640e..5529fe7 100644 (file)
-#!/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
index 34f4dce..70f8830 100644 (file)
@@ -1,47 +1,66 @@
-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
index d438e24..d6734e6 100755 (executable)
@@ -64,7 +64,7 @@ class XmlElement:
             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)
@@ -265,7 +265,7 @@ class XML:
         if not element:
             element = self.root
         element.remove_attribute(name) 
-        
+
     def add_element(self, *args, **kwds):
         """
         Wrapper around etree.SubElement(). Adds an element to 
index 08a257d..985b571 100644 (file)
@@ -22,7 +22,7 @@
 #----------------------------------------------------------------------
 
 import re
-import sys
+
 from sfa.util.faults import SfaAPIError
 
 # for convenience and smoother translation - we should get rid of these functions eventually 
@@ -82,7 +82,7 @@ class Xrn:
     # 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:
@@ -121,7 +121,6 @@ class Xrn:
     # 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
@@ -132,6 +131,7 @@ class Xrn:
             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")
@@ -155,8 +155,7 @@ class Xrn:
         # 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
@@ -168,19 +167,20 @@ class Xrn:
     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
@@ -226,7 +226,7 @@ class Xrn:
 
         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("+")
@@ -234,7 +234,7 @@ class Xrn:
             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)