merged master onto senslab2, manually solved conflicts in setup.py
Thierry Parmentelat [Thu, 3 Nov 2011 14:59:18 +0000 (15:59 +0100)]
(under control) and rspec.py (now in sfa/rspecs), the latter being the
mainstream version as is

147 files changed:
.gitignore
INSTALL.txt
Makefile
config/default_config.xml
config/topology [new file with mode: 0644]
docs/Makefile
setup.py
sfa.spec
sfa/client/getNodes.py
sfa/client/getRecord.py
sfa/client/setRecord.py
sfa/client/sfadump.py
sfa/client/sfi.py
sfa/client/sfiAddAttribute.py
sfa/client/sfiAddLinks.py [new file with mode: 0755]
sfa/client/sfiAddSliver.py
sfa/client/sfiListLinks.py [new file with mode: 0755]
sfa/client/sfi_commands.py
sfa/generic/__init__.py [new file with mode: 0644]
sfa/generic/pl.py [new file with mode: 0644]
sfa/generic/plcm.py [new file with mode: 0644]
sfa/init.d/sfa
sfa/init.d/sfa-cm
sfa/managers/aggregate_manager.py [moved from sfa/managers/aggregate_manager_pl.py with 88% similarity]
sfa/managers/aggregate_manager_eucalyptus.py
sfa/managers/aggregate_manager_max.py
sfa/managers/aggregate_manager_openflow.py
sfa/managers/aggregate_manager_vini.py [deleted file]
sfa/managers/component_manager_pl.py
sfa/managers/managerwrapper.py [new file with mode: 0644]
sfa/managers/registry_manager.py [moved from sfa/managers/registry_manager_pl.py with 96% similarity]
sfa/managers/slice_manager.py [moved from sfa/managers/slice_manager_pl.py with 94% similarity]
sfa/managers/vini/topology.py
sfa/methods/CreateGid.py
sfa/methods/CreateSliver.py
sfa/methods/DeleteSliver.py
sfa/methods/GetCredential.py
sfa/methods/GetGids.py
sfa/methods/GetSelfCredential.py
sfa/methods/GetTicket.py
sfa/methods/GetVersion.py
sfa/methods/List.py
sfa/methods/ListResources.py
sfa/methods/ListSlices.py
sfa/methods/RedeemTicket.py
sfa/methods/Register.py
sfa/methods/RegisterPeerObject.py
sfa/methods/Remove.py
sfa/methods/RemovePeerObject.py
sfa/methods/RenewSliver.py
sfa/methods/Resolve.py
sfa/methods/ResolveGENI.py
sfa/methods/Shutdown.py
sfa/methods/SliverStatus.py
sfa/methods/Start.py
sfa/methods/Stop.py
sfa/methods/Update.py
sfa/methods/UpdateSliver.py
sfa/methods/get_aggregates.py
sfa/methods/get_key.py
sfa/methods/get_registries.py
sfa/methods/get_trusted_certs.py
sfa/methods/register_peer_object.py
sfa/methods/remove_peer_object.py
sfa/methods/reset_slice.py
sfa/plc/aggregate.py
sfa/plc/api.py [deleted file]
sfa/plc/network.py
sfa/plc/plccomponentapi.py [new file with mode: 0644]
sfa/plc/plcsfaapi.py [new file with mode: 0644]
sfa/plc/sfa-import-plc.py
sfa/plc/sfa-nuke-plc.py
sfa/plc/sfaImport.py
sfa/plc/slices.py
sfa/plc/vlink.py [new file with mode: 0644]
sfa/rspecs/elements/interface.py [new file with mode: 0644]
sfa/rspecs/elements/link.py
sfa/rspecs/elements/versions/__init__.py [new file with mode: 0644]
sfa/rspecs/elements/versions/pgv2Link.py [new file with mode: 0644]
sfa/rspecs/pg_rspec_converter.py
sfa/rspecs/rspec.py
sfa/rspecs/rspec_elements.py
sfa/rspecs/sfa_rspec_converter.py
sfa/rspecs/versions/pgv2.py
sfa/rspecs/versions/sfav1.py
sfa/rspecs/xml_interface.py [deleted file]
sfa/server/aggregate.py
sfa/server/component.py
sfa/server/interface.py
sfa/server/modpython/SfaAggregateModPython.py
sfa/server/modpython/SfaRegistryModPython.py
sfa/server/modpython/SfaSliceMgrModPython.py
sfa/server/registry.py
sfa/server/sfa-ca.py
sfa/server/sfa-clean-peer-records.py
sfa/server/sfa-start.py [moved from sfa/server/sfa-server.py with 88% similarity]
sfa/server/sfa_component_setup.py
sfa/server/sfaapi.py [new file with mode: 0644]
sfa/server/sfaserver.py [new file with mode: 0644]
sfa/server/slicemgr.py
sfa/server/threadedserver.py [moved from sfa/util/server.py with 80% similarity]
sfa/server/xmlrpcapi.py [new file with mode: 0644]
sfa/trust/auth.py
sfa/trust/certificate.py
sfa/trust/credential.py
sfa/trust/credential_legacy.py
sfa/trust/gid.py
sfa/trust/hierarchy.py
sfa/trust/sfaticket.py [moved from sfa/util/sfaticket.py with 98% similarity]
sfa/util/PostgreSQL.py
sfa/util/api.py [deleted file]
sfa/util/bwlimit.py [deleted file]
sfa/util/cache.py
sfa/util/componentserver.py [deleted file]
sfa/util/config.py
sfa/util/defaultdict.py [new file with mode: 0644]
sfa/util/faults.py
sfa/util/filter.py
sfa/util/httpsProtocol.py [deleted file]
sfa/util/method.py
sfa/util/parameter.py
sfa/util/plxrn.py
sfa/util/policy.py
sfa/util/record.py
sfa/util/rspec.py [deleted file]
sfa/util/rspecHelper.py
sfa/util/specdict.py [deleted file]
sfa/util/storage.py
sfa/util/table.py
sfa/util/topology.py [new file with mode: 0644]
sfa/util/xml.py [moved from sfa/rspecs/xml.py with 68% similarity]
sfa/util/xmlrpcprotocol.py
sfa/util/xrn.py
sfatables/commands/Add.py
sfatables/commands/Delete.py
sfatables/commands/Insert.py
sfatables/commands/List.py
sfatables/runtime.py
sfatables/sfatables
sfatables/xmlextension.py
sfatables/xmlrule.py
tests/client/README [deleted file]
tests/testInterfaces.py
tools/Makefile [new file with mode: 0644]
tools/depgraph2dot.py [new file with mode: 0755]
tools/py2depgraph.py [new file with mode: 0755]
tools/readme [new file with mode: 0644]

index 9acfb22..7e5b62e 100644 (file)
@@ -15,3 +15,4 @@ sfa/client/*.version
 *.pkey
 *.cert
 *.cred
+.DS_Store
index d748883..cc59ceb 100644 (file)
@@ -63,9 +63,9 @@ This will initialize /etc/sfa/authorities/server.key from /etc/sfa/authorities/p
 This will start Registry, Slice Manager and Aggregate Manager. Your ps command output would look like:
 
 # ps -ef | grep python
-root     24944     1  0 May11 ?        00:00:00 /usr/bin/python /usr/bin/sfa-server.py -r -d
-root     24957     1  0 May11 ?        00:00:00 /usr/bin/python /usr/bin/sfa-server.py -a -d
-root     24970     1  0 May11 ?        00:00:00 /usr/bin/python /usr/bin/sfa-server.py -s -d
+root     24944     1  0 May11 ?        00:00:00 /usr/bin/python /usr/bin/sfa-start.py -r -d
+root     24957     1  0 May11 ?        00:00:00 /usr/bin/python /usr/bin/sfa-start.py -a -d
+root     24970     1  0 May11 ?        00:00:00 /usr/bin/python /usr/bin/sfa-start.py -s -d
 -------
 4) Configure SFA client:
 
index f162f5f..04ed4ba 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -87,7 +87,7 @@ force:
 
 ##########
 tags:  
-       find . -type f | egrep -v '/\.git/|/\.svn/|TAGS|\.py[co]$$|\.doc$$|\.html$$|\.pdf$$|~$$|\.png$$|\.svg$$|\.out$$|\.bak$$|\.xml$$' | xargs etags
+       find . -type f | egrep -v '/\.git/|/\.svn/|TAGS|~$$|\.(py[co]|doc|html|pdf|png|svg|out|bak|xml|dg)$$' | xargs etags
 .PHONY: tags
 
 signatures:
@@ -125,7 +125,7 @@ sfiAddAttribute.py sfiAddSliver.py sfiDeleteAttribute.py sfiDeleteSliver.py sfiL
 sfiListSlivers.py sfadump.py
 
 BINS = ./config/sfa-config-tty ./config/gen-sfa-cm-config.py \
-       ./sfa/plc/sfa-import-plc.py ./sfa/plc/sfa-nuke-plc.py ./sfa/server/sfa-server.py \
+       ./sfa/plc/sfa-import-plc.py ./sfa/plc/sfa-nuke-plc.py ./sfa/server/sfa-start.py \
        $(foreach client,$(CLIENTS),./sfa/client/$(client))
 
 sync:
@@ -137,7 +137,9 @@ ifeq (,$(SSHURL))
 else
        +$(RSYNC) ./sfa/ $(SSHURL)/usr/lib\*/python2.\*/site-packages/sfa/
        +$(RSYNC) ./tests/ $(SSHURL)/root/tests-sfa
-       +$(RSYNC)  $(BINS) $(SSHURL)/usr/bin
+       +$(RSYNC)  $(BINS) $(SSHURL)/usr/bin/
+       +$(RSYNC) ./sfa/init.d/sfa  $(SSHURL)/etc/init.d/
+       +$(RSYNC) ./config/default_config.xml $(SSHURL)/etc/sfa/
        $(SSHCOMMAND) exec service sfa restart
 endif
 
index 212dee4..670d6f2 100644 (file)
@@ -18,6 +18,15 @@ Thierry Parmentelat
       <description>Basic system variables.</description>
 
       <variablelist>
+       <variable id="generic_flavour" type="string">
+         <name>Generic Flavour</name>
+         <value>pl</value>
+         <description>This string refers to a class located in sfa.generic that describes 
+         which specific implementation needs to be used for api, manager and driver objects.
+         PlanetLab users do not need to change this setting.
+         </description>
+       </variable>
+
         <variable id="interface_hrn" type="string">
           <name>Human readable name</name>
           <value>plc</value>
@@ -49,9 +58,10 @@ Thierry Parmentelat
             it look like the user is the one performing the operation. Doing this requires a 
             valid key pair and credential for the user. This option defines the path where 
             key pairs and credentials are generated and stored.
-            This functionality is used by the SFA web gui 
+            This functionality is used by the SFA web GUI. 
             </description> 
         </variable>
+
       </variablelist>
     </category>
 
diff --git a/config/topology b/config/topology
new file mode 100644 (file)
index 0000000..24a8e13
--- /dev/null
@@ -0,0 +1,20 @@
+# Links in the physical topology, gleaned from looking at the Internet2
+# topology map.  Link (a, b) connects sites with IDs a and b.
+#
+# 2 12  # I2 Princeton - New York
+# 11 13 # I2 Chicago - Wash DC
+# 11 15 # I2 Chicago - Atlanta
+# 11 16 # I2 Chicago - CESNET
+# 11 17 # I2 Chicago - Kansas City
+# 12 13 # I2 New York - Wash DC
+# 13 15 # I2 Wash DC - Atlanta
+# 14 15 # Ga Tech - I2 Atlanta
+# 15 19 # I2 Atlanta - Houston
+# 17 19 # I2 Kansas City - Houston
+# 17 22 # I2 Kansas City - Salt Lake City
+# 17 24 # I2 Kansas City - UMKC
+# 19 20 # I2 Houston - Los Angeles
+# 20 21 # I2 Los Angeles - Seattle
+# 20 22 # I2 Los Angeles - Salt Lake City
+# 21 22 # I2 Seattle - Salt Lake City
+
index 463dbaf..5f34949 100644 (file)
@@ -3,7 +3,7 @@ doc:
        pythondoc.py ../sfa/util/certificate.py ../sfa/util/credential.py ../sfa/util/gid.py \
                      ../sfa/util/rights.py ../sfa/util/config.py ../sfa/trust/hierarchy.py \
                      ../sfa/util/record.py ../sfa/util/client.py \
-                     ../sfa/util/server.py 
+                     ../sfa/server/sfaserver.py 
 
        pythondoc.py ../sfa/registry/registry.py ../sfa/registry/import.py \
                      ../sfa/registry/nuke.py
index 921139c..d6fd9e1 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@ bins = [
     'sfa/plc/sfa-import-plc.py', 
     'sfa/plc/sfa-nuke-plc.py', 
     'sfa/server/sfa-ca.py', 
-    'sfa/server/sfa-server.py', 
+    'sfa/server/sfa-start.py', 
     'sfa/server/sfa-clean-peer-records.py', 
     'sfa/server/sfa_component_setup.py', 
     'sfa/client/sfi.py', 
@@ -36,17 +36,19 @@ bins = [
 
 package_dirs = [
     'sfa', 
-    'sfa/client',
-    'sfa/methods',
-    'sfa/plc',
-    'sfa/senslab',
-    'sfa/server',
     'sfa/trust',
     'sfa/util', 
+    'sfa/client',
+    'sfa/server',
+    'sfa/methods',
+    'sfa/generic',
     'sfa/managers',
     'sfa/managers/vini',
+    'sfa/plc',
+    'sfa/senslab',
     'sfa/rspecs',
     'sfa/rspecs/elements',
+    'sfa/rspecs/elements/versions',
     'sfa/rspecs/versions',
     'sfatables',
     'sfatables/commands',
@@ -59,6 +61,7 @@ data_files = [('/etc/sfa/', [ 'config/aggregates.xml',
                               'config/registries.xml',
                               'config/default_config.xml',
                               'config/sfi_config',
+                              'config/topology',
                               'sfa/managers/pl/pl.rng',
                               'sfa/trust/credential.xsd',
                               'sfa/trust/top.xsd',
index 95e56a3..d2979e4 100644 (file)
--- a/sfa.spec
+++ b/sfa.spec
@@ -1,6 +1,6 @@
 %define name sfa
-%define version 1.0
-%define taglevel 36
+%define version 1.1
+%define taglevel 1
 
 %define release %{taglevel}%{?pldistro:.%{pldistro}}%{?date:.%{date}}
 %global python_sitearch        %( python -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)" )
@@ -46,13 +46,13 @@ Requires: python-dateutil
 #%endif
 
 %package cm
-Summary: the SFA wrapper around MyPLC NodeManager
+Summary: the SFA layer around MyPLC NodeManager
 Group: Applications/System
 Requires: sfa
 Requires: pyOpenSSL >= 0.6
 
 %package plc
-Summary: the SFA wrapper arounf MyPLC
+Summary: the SFA layer around MyPLC
 Group: Applications/System
 Requires: sfa
 Requires: python-psycopg2
@@ -121,7 +121,7 @@ rm -rf $RPM_BUILD_ROOT
 
 %files
 # sfa and sfatables depend each other.
-%{_bindir}/sfa-server.py*
+%{_bindir}/sfa-start.py*
 /etc/sfatables/*
 %{python_sitelib}/*
 %{_bindir}/keyconvert.py*
@@ -144,6 +144,7 @@ rm -rf $RPM_BUILD_ROOT
 /etc/sfa/sig.xsd
 /etc/sfa/xml.xsd
 /etc/sfa/protogeni-rspec-common.xsd
+/etc/sfa/topology
 %{_bindir}/sfa-config-tty
 %{_bindir}/sfa-import-plc.py*
 %{_bindir}/sfa-clean-peer-records.py*
@@ -196,6 +197,11 @@ fi
 [ "$1" -ge "1" ] && service sfa-cm restart || :
 
 %changelog
+* Fri Oct 28 2011 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - sfa-1.1-1
+- first support for protogeni rspecs is working
+- vini no longer needs a specific manager
+- refactoring underway towards more flexible/generic architecture
+
 * Thu Sep 15 2011 Tony Mack <tmack@cs.princeton.edu> - sfa-1.0-36
 - Unicode-friendliness for user names with accents/special chars.
 - Fix bug that could cause create the client to fail when calling CreateSliver for a slice that has the same hrn as a user.
index 67f9a28..71d17f0 100644 (file)
@@ -6,8 +6,6 @@ from optparse import OptionParser
 from pprint import pprint
 from types import StringTypes
 
-from sfa.util.rspec import RSpec
-
 def create_parser():
     command = sys.argv[0]
     argv = sys.argv[1:]
index cb765e0..e2be593 100755 (executable)
@@ -14,9 +14,7 @@ import os
 from optparse import OptionParser
 from pprint import pprint
 from xml.parsers.expat import ExpatError
-
-from sfa.util.rspec import RecordSpec
-
+from sfa.util.xml import XML    
 
 def create_parser():
     command = sys.argv[0]
@@ -34,17 +32,17 @@ def create_parser():
     return parser    
 
 
-def printRec(record, filters, options):
+def printRec(record_dict, filters, options):
     line = ""
     if len(filters):
         for filter in filters:
             if options.DEBUG:  print "Filtering on %s" %filter
             line += "%s: %s\n" % (filter, 
-                printVal(record.dict["record"].get(filter, None)))
+                printVal(record_dict.get(filter, None)))
         print line
     else:
         # print the wole thing
-        for (key, value) in record.dict["record"].iteritems():
+        for (key, value) in record_dict.iteritems():
             if (not options.withkey and key in ('gid', 'keys')) or\
                 (not options.plinfo and key == 'pl_info'):
                 continue
@@ -69,16 +67,14 @@ def main():
 
     stdin = sys.stdin.read()
     
-    record = RecordSpec(xml = stdin)
+    record = XML(stdin)
+    record_dict = record.todict()
     
-    if not record.dict.has_key("record"):
-        raise "RecordError", "Input record does not have 'record' tag."
-
     if options.DEBUG: 
-        record.pprint()
+        pprint(record.toxml())
         print "#####################################################"
 
-    printRec(record, args, options)
+    printRec(record_dict, args, options)
 
 if __name__ == '__main__':
     try: main()
index 5f48e68..405c90d 100755 (executable)
@@ -14,9 +14,7 @@ sys.path.append('.')
 import os
 from optparse import OptionParser
 from pprint import pprint
-
-from sfa.util.rspec import RecordSpec
-
+from sfa.util.xml import XML
 
 def create_parser():
     command = sys.argv[0]
@@ -92,15 +90,14 @@ def main():
     parser = create_parser(); 
     (options, args) = parser.parse_args()
 
-    record = RecordSpec(xml = sys.stdin.read())
-
+    record = XML(sys.stdin.read())
+    record_dict = record.todict()
     if args:
-        editDict(args, record.dict["record"], options)
+        editDict(args, record_dict, options)
     if options.DEBUG:
-        print "New Record:\n%s" % record.dict
-        record.pprint()
-
-    record.parseDict(record.dict)
+        print "New Record:\n%s" % record_dict
+        
+    record.parse_dict(record_dict)
     s = record.toxml()
     sys.stdout.write(s)
 
index 54654f8..52a9105 100755 (executable)
@@ -12,7 +12,6 @@ from sfa.trust.certificate import Certificate
 from sfa.trust.credential import Credential
 from sfa.trust.gid import GID
 from sfa.util.record import SfaRecord
-from sfa.util.rspec import RSpec
 from sfa.util.sfalogging import logger
 
 def determine_sfa_filekind(fn):
index 83a66f9..4939246 100755 (executable)
@@ -18,7 +18,7 @@ from sfa.util.sfalogging import sfi_logger
 from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.gid import GID
 from sfa.trust.credential import Credential
-from sfa.util.sfaticket import SfaTicket
+from sfa.trust.sfaticket import SfaTicket
 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
 from sfa.rspecs.rspec import RSpec
 from sfa.rspecs.rspec_converter import RSpecConverter
@@ -232,9 +232,9 @@ class Sfi:
             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
                              action="store_true",
                              help="Include a credential delegated to the user's root"+\
-                                  "authority in set of credentials for this call")  
-        
-        # registy filter option    
+                                  "authority in set of credentials for this call")
+
+        # registy filter option
         if command in ("list", "show", "remove"):
             parser.add_option("-t", "--type", dest="type", type="choice",
                             help="type filter ([all]|user|slice|authority|node|aggregate)",
@@ -521,7 +521,7 @@ class Sfi:
         if args:
             hrn = args[0]
         gid = self._get_gid(hrn)
-        self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
+        self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
         return gid
 
     def _get_gid(self, hrn=None, type=None):
@@ -985,10 +985,15 @@ class Sfi:
         slice_urn = hrn_to_urn(slice_hrn, 'slice')
         user_cred = self.get_user_cred()
         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
-        # delegate the cred to the callers root authority
-        delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
-        #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
-        #creds.append(delegated_cred)
+
+        if hasattr(opts, 'aggregate') and opts.aggregate:
+            delegated_cred = None
+        else:
+            # delegate the cred to the callers root authority
+            delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
+            #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
+            #creds.append(delegated_cred)
+
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
 
@@ -1013,11 +1018,13 @@ class Sfi:
                 creds = [slice_cred]
             else:
                 users = sfa_users_arg(user_records, slice_record)
-                creds = [slice_cred, delegated_cred]
+                creds = [slice_cred]
+                if delegated_cred:
+                    creds.append(delegated_cred)
         call_args = [slice_urn, creds, rspec, users]
         if self.server_supports_call_id_arg(server):
             call_args.append(unique_call_id())
-           
+
         result = server.CreateSliver(*call_args)
         if opts.file is None:
             print result
index 9c2eae5..f22e63e 100755 (executable)
@@ -1,7 +1,6 @@
 #! /usr/bin/env python
 
 import sys
-from sfa.util.rspecHelper import RSpec, Commands
 from sfa.client.sfi_commands import Commands
 from sfa.rspecs.rspec import RSpec
 
diff --git a/sfa/client/sfiAddLinks.py b/sfa/client/sfiAddLinks.py
new file mode 100755 (executable)
index 0000000..f5b2888
--- /dev/null
@@ -0,0 +1,45 @@
+#! /usr/bin/env python
+
+import sys
+from sfa.client.sfi_commands import Commands
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.version_manager import VersionManager
+
+command = Commands(usage="%prog [options] node1 node2...",
+                   description="Add links to the RSpec. " +
+                   "This command reads in an RSpec and outputs a modified " +
+                   "RSpec. Use this to add links to your slivers")
+command.add_linkfile_option()
+command.prep()
+
+if not command.opts.linkfile:
+    print "Missing link list -- exiting"
+    command.parser.print_help()
+    sys.exit(1)
+    
+if command.opts.infile:
+    infile=file(command.opts.infile)
+else:
+    infile=sys.stdin
+if command.opts.outfile:
+    outfile=file(command.opts.outfile,"w")
+else:
+    outfile=sys.stdout
+ad_rspec = RSpec(infile)
+links = file(command.opts.linkfile).read().split('\n')
+link_tuples = map(lambda x: tuple(x.split()), links)
+
+version_manager = VersionManager()
+try:
+    type = ad_rspec.version.type
+    version_num = ad_rspec.version.version
+    request_version = version_manager._get_version(type, version_num, 'request')    
+    request_rspec = RSpec(version=request_version)
+    request_rspec.version.merge(ad_rspec)
+    request_rspec.version.add_link_requests(link_tuples)
+except:
+    print >> sys.stderr, "FAILED: %s" % links
+    raise
+    sys.exit(1)
+print >>outfile, request_rspec.toxml()
+sys.exit(0)
index ef4a008..c72dee3 100755 (executable)
@@ -25,19 +25,20 @@ if command.opts.outfile:
     outfile=file(command.opts.outfile,"w")
 else:
     outfile=sys.stdout
-request_rspec = RSpec(infile)
+ad_rspec = RSpec(infile)
 nodes = file(command.opts.nodefile).read().split()
 version_manager = VersionManager()
 try:
-    type = request_rspec.version.type
-    version_num = request_rspec.version.version
-    manifest_version = version_manager._get_version(type, version_num, 'manifest')    
-    manifest_rspec = RSpec(version=manifest_version)
+    type = ad_rspec.version.type
+    version_num = ad_rspec.version.version
+    request_version = version_manager._get_version(type, version_num, 'request')    
+    request_rspec = RSpec(version=request_version)
     slivers = [{'hostname': node} for node in nodes]
-    manifest_rspec.version.merge(request_rspec)
-    manifest_rspec.version.add_slivers(slivers)
+    request_rspec.version.merge(ad_rspec)
+    request_rspec.version.add_slivers(slivers)
 except:
     print >> sys.stderr, "FAILED: %s" % nodes
+    raise
     sys.exit(1)
-print >>outfile, manifest_rspec.toxml()
+print >>outfile, request_rspec.toxml()
 sys.exit(0)
diff --git a/sfa/client/sfiListLinks.py b/sfa/client/sfiListLinks.py
new file mode 100755 (executable)
index 0000000..a4720ca
--- /dev/null
@@ -0,0 +1,26 @@
+#! /usr/bin/env python
+
+import sys
+from sfa.client.sfi_commands import Commands
+from sfa.rspecs.rspec import RSpec
+from sfa.util.xrn import Xrn 
+
+command = Commands(usage="%prog [options]",
+                   description="List all links in the RSpec. " + 
+                   "Use this to display the list of available links. " ) 
+command.prep()
+
+if command.opts.infile:
+    rspec = RSpec(command.opts.infile)
+    links = rspec.version.get_links()
+    if command.opts.outfile:
+        sys.stdout = open(command.opts.outfile, 'w')
+    
+    for link in links:
+        ifname1 = Xrn(link['interface1']['component_id']).get_leaf()
+        ifname2 = Xrn(link['interface2']['component_id']).get_leaf()
+        print "%s %s" % (ifname1, ifname2)
+
+
+
+    
index bdcc16d..80897cd 100755 (executable)
@@ -12,6 +12,7 @@ class Commands:
         self.parser.add_option("-o", "", dest="outfile", metavar="FILE",
                                help="write output to FILE (default is stdout)")
         self.nodefile = False
+        self.linkfile = False
         self.attributes = {}
 
     def add_nodefile_option(self):
@@ -20,6 +21,12 @@ class Commands:
                                metavar="FILE",
                                help="read node list from FILE"),
 
+    def add_linkfile_option(self):
+        self.linkfile = True
+        self.parser.add_option("-l", "", dest="linkfile",
+                               metavar="FILE",
+                               help="read link list from FILE") 
+
     def add_show_attributes_option(self):
         self.parser.add_option("-s", "--show-attributes", action="store_true", 
                                dest="showatt", default=False, 
diff --git a/sfa/generic/__init__.py b/sfa/generic/__init__.py
new file mode 100644 (file)
index 0000000..843cd7b
--- /dev/null
@@ -0,0 +1,104 @@
+from sfa.util.sfalogging import logger
+from sfa.util.config import Config
+
+from sfa.managers.managerwrapper import ManagerWrapper
+
+# a bundle is the combination of 
+# (*) an api that reacts on the incoming requests to trigger the API methods
+# (*) a manager that implements the function of the service, 
+#     either aggregate, registry, or slicemgr
+# (*) a driver that controls the underlying testbed
+# 
+# 
+# The Generic class is a utility that uses the configuration to figure out 
+# which combination of these pieces need to be put together 
+# from config.
+# this extra indirection is needed to adapt to the current naming scheme
+# where we have 'pl' and 'plc' and components and the like, that does not 
+# yet follow a sensible scheme
+
+# needs refinements to cache more efficiently, esp. wrt the config
+
+class Generic:
+
+    def __init__ (self, flavour, config):
+        self.flavour=flavour
+        self.config=config
+
+    # proof of concept
+    # example flavour='pl' -> sfa.generic.pl.pl()
+    @staticmethod
+    def the_flavour (flavour=None, config=None):
+        if config is None: config=Config()
+        if flavour is None: flavour=config.SFA_GENERIC_FLAVOUR
+        flavour = flavour.lower()
+        #mixed = flavour.capitalize()
+        module_path="sfa.generic.%s"%flavour
+        classname="%s"%flavour
+        logger.info("Generic.the_flavour with flavour=%s"%flavour)
+        try:
+            module = __import__ (module_path, globals(), locals(), [classname])
+            return getattr(module, classname)(flavour,config)
+        except:
+            logger.log_exc("Cannot locate generic instance with flavour=%s"%flavour)
+
+    # in the simplest case these can be redefined to the class/module objects to be used
+    # see pl.py for an example
+    # some descendant of SfaApi
+    def api_class (self) : pass
+    # in practical terms these are modules for now
+    def registry_class (self) : pass
+    def slicemgr_class (self) : pass
+    def aggregate_class (self) : pass
+    def component_class (self) : pass
+
+
+    # build an API object
+    # insert a manager instance 
+    def make_api (self, *args, **kwargs):
+        # interface is a required arg
+        if not 'interface' in kwargs:
+            logger.fatal("Generic.make_api: no interface found")
+        api = self.api_class()(*args, **kwargs)
+        interface=kwargs['interface']
+        # or simpler, interface=api.interface
+        manager = self.make_manager(interface)
+        api.manager = ManagerWrapper(manager,interface)
+        return api
+
+    def make_manager (self, interface):
+        """
+        interface expected in ['registry', 'aggregate', 'slice', 'component']
+        flavour is e.g. 'pl' or 'max' or whatever
+        """
+        flavour = self.flavour
+        message="Generic.make_manager for interface=%s and flavour=%s"%(interface,flavour)
+        
+        classname = "%s_class"%interface
+        try:
+            module = getattr(self,classname)()
+            logger.info("%s : %s"%(message,module))
+            return module
+        except:
+            logger.log_exc(message)
+            logger.fatal("Aborting")
+        
+# former logic was
+#        basepath = 'sfa.managers'
+#        qualified = "%s.%s_manager_%s"%(basepath,interface,flavour)
+#        generic = "%s.%s_manager"%(basepath,interface)
+#
+#        try: 
+#            manager = __import__(qualified, fromlist=[basepath])
+#            logger.info ("%s: loaded %s"%(message,qualified))
+#        except:
+#            try:
+#                manager = __import__ (generic, fromlist=[basepath])
+#                if flavour != 'pl' : 
+#                    logger.warn ("%s: using generic with flavour!='pl'"%(message))
+#                logger.info("%s: loaded %s"%(message,generic))
+#            except:
+#                logger.log_exc("%s: unable to import either %s or %s"%(message,qualified,generic))
+#                logger.fatal("Aborted")
+#        return manager
+        
diff --git a/sfa/generic/pl.py b/sfa/generic/pl.py
new file mode 100644 (file)
index 0000000..853053d
--- /dev/null
@@ -0,0 +1,19 @@
+from sfa.generic import Generic
+import sfa.plc.plcsfaapi
+import sfa.managers.registry_manager
+import sfa.managers.slice_manager
+import sfa.managers.aggregate_manager
+
+class pl (Generic):
+    
+    def api_class (self):
+        return sfa.plc.plcsfaapi.PlcSfaApi
+
+    def registry_class (self) : 
+        return sfa.managers.registry_manager
+    def slicemgr_class (self) : 
+        return sfa.managers.slice_manager
+    def aggregate_class (self) :
+        return sfa.managers.aggregate_manager
+
+
diff --git a/sfa/generic/plcm.py b/sfa/generic/plcm.py
new file mode 100644 (file)
index 0000000..dd24d3c
--- /dev/null
@@ -0,0 +1,11 @@
+from sfa.generic.pl import pl
+import sfa.plc.plccomponentapi
+import sfa.managers.component_manager_pl
+
+class plcm (pl):
+
+    def api_class (self):
+        return sfa.plc.plccomponentapi.PlcComponentApi
+
+    def component_class (self):
+        return sfa.managers.component_manager_pl
index e2fdb10..08975dc 100755 (executable)
@@ -62,18 +62,18 @@ start() {
     reload
 
     # install peer certs
-    action $"SFA installing peer certs" daemon /usr/bin/sfa-server.py -t -d $OPTIONS 
+    action $"SFA installing peer certs" daemon /usr/bin/sfa-start.py -t -d $OPTIONS 
 
     if [ "$SFA_REGISTRY_ENABLED" -eq 1 ]; then
-        action $"SFA Registry" daemon /usr/bin/sfa-server.py -r -d $OPTIONS
+        action $"SFA Registry" daemon /usr/bin/sfa-start.py -r -d $OPTIONS
     fi
 
     if [ "$SFA_AGGREGATE_ENABLED" -eq 1 ]; then
-        action $"SFA Aggregate" daemon /usr/bin/sfa-server.py -a -d $OPTIONS
+        action $"SFA Aggregate" daemon /usr/bin/sfa-start.py -a -d $OPTIONS
     fi
         
     if [ "$SFA_SM_ENABLED" -eq 1 ]; then
-        action "SFA SliceMgr" daemon /usr/bin/sfa-server.py -s -d $OPTIONS
+        action "SFA SliceMgr" daemon /usr/bin/sfa-start.py -s -d $OPTIONS
     fi
 
     if [ "$SFA_FLASHPOLICY_ENABLED" -eq 1 ]; then
@@ -81,15 +81,15 @@ start() {
     fi
 
     RETVAL=$?
-    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sfa-server.py
+    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sfa-start.py
 
 }
 
 stop() {
-    action $"Shutting down SFA" killproc sfa-server.py
+    action $"Shutting down SFA" killproc sfa-start.py
     RETVAL=$?
 
-    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sfa-server.py
+    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sfa-start.py
 }
 
 
@@ -99,13 +99,13 @@ case "$1" in
     reload) reload force ;;
     restart) stop; start ;;
     condrestart)
-       if [ -f /var/lock/subsys/sfa-server.py ]; then
+       if [ -f /var/lock/subsys/sfa-start.py ]; then
             stop
             start
        fi
        ;;
     status)
-       status sfa-server.py
+       status sfa-start.py
        RETVAL=$?
        ;;
     *)
index e3bbd96..cdddf8b 100755 (executable)
@@ -6,9 +6,6 @@
 #
 # description:   Wraps PLCAPI into the SFA compliant API
 #
-# $Id: sfa 14304 2009-07-06 20:19:51Z thierry $
-# $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/init.d/sfa $
-#
 
 # Source config
 . /etc/sfa/sfa_config
@@ -30,7 +27,7 @@ start() {
             echo "Component Mgr"
             # make sure server key (nodes private key) exists first
             init_key
-            /usr/bin/sfa-server.py -c -d $OPTIONS
+            /usr/bin/sfa-start.py -c -d $OPTIONS
         fi
 
         RETVAL=$?
@@ -41,7 +38,7 @@ start() {
 
 stop() {
     echo -n $"Shutting down SFA: "
-    killproc sfa-server.py
+    killproc sfa-start.py
     RETVAL=$?
 
     echo
similarity index 88%
rename from sfa/managers/aggregate_manager_pl.py
rename to sfa/managers/aggregate_manager.py
index e61efc7..702fe7d 100644 (file)
@@ -1,32 +1,24 @@
 import datetime
 import time
-import traceback
 import sys
-import re
-from types import StringTypes
 
-from sfa.util.faults import *
+from sfa.util.faults import RecordNotFound, SliverDoesNotExist
 from sfa.util.xrn import get_authority, hrn_to_urn, urn_to_hrn, Xrn, urn_to_sliver_id
-from sfa.util.plxrn import slicename_to_hrn, hrn_to_pl_slicename, hostname_to_urn
-from sfa.util.rspec import *
-from sfa.util.specdict import *
-from sfa.util.record import SfaRecord
-from sfa.util.policy import Policy
-from sfa.util.record import *
-from sfa.util.sfaticket import SfaTicket
-from sfa.plc.slices import Slices
-from sfa.trust.credential import Credential
-import sfa.plc.peers as peers
-from sfa.plc.network import *
-from sfa.plc.api import SfaAPI
-from sfa.plc.aggregate import Aggregate
-from sfa.plc.slices import *
+from sfa.util.plxrn import slicename_to_hrn, hrn_to_pl_slicename
 from sfa.util.version import version_core
-from sfa.rspecs.version_manager import VersionManager
-from sfa.rspecs.rspec import RSpec
 from sfa.util.sfatime import utcparse
 from sfa.util.callids import Callids
 
+from sfa.trust.sfaticket import SfaTicket
+from sfa.trust.credential import Credential
+from sfa.rspecs.version_manager import VersionManager
+from sfa.rspecs.rspec import RSpec
+
+import sfa.plc.peers as peers
+from sfa.plc.plcsfaapi import PlcSfaApi
+from sfa.plc.aggregate import Aggregate
+from sfa.plc.slices import Slices
+
 def GetVersion(api):
 
     version_manager = VersionManager()
@@ -52,7 +44,7 @@ def __get_registry_objects(slice_xrn, creds, users):
     """
 
     """
-    hrn, type = urn_to_hrn(slice_xrn)
+    hrn, _ = urn_to_hrn(slice_xrn)
 
     hrn_auth = get_authority(hrn)
 
@@ -116,7 +108,7 @@ def __get_hostnames(nodes):
 def SliverStatus(api, slice_xrn, creds, call_id):
     if Callids().already_handled(call_id): return {}
 
-    (hrn, type) = urn_to_hrn(slice_xrn)
+    (hrn, _) = urn_to_hrn(slice_xrn)
     # find out where this slice is currently running
     slicename = hrn_to_pl_slicename(hrn)
     
@@ -129,8 +121,6 @@ def SliverStatus(api, slice_xrn, creds, call_id):
     nodes = api.plshell.GetNodes(api.plauth, {'node_id':slice['node_ids'],'peer_id':None},
                                  ['node_id', 'hostname', 'site_id', 'boot_state', 'last_contact'])
     site_ids = [node['site_id'] for node in nodes]
-    sites = api.plshell.GetSites(api.plauth, site_ids, ['site_id', 'login_base'])
-    sites_dict = dict ( [ (site['site_id'],site['login_base'] ) for site in sites ] )
 
     result = {}
     top_level_status = 'unknown'
@@ -155,7 +145,7 @@ def SliverStatus(api, slice_xrn, creds, call_id):
             res['geni_status'] = 'ready'
         else:
             res['geni_status'] = 'failed'
-            top_level_staus = 'failed' 
+            top_level_status = 'failed' 
             
         res['geni_error'] = ''
 
@@ -174,7 +164,7 @@ def CreateSliver(api, slice_xrn, creds, rspec_string, users, call_id):
 
     aggregate = Aggregate(api)
     slices = Slices(api)
-    (hrn, type) = urn_to_hrn(slice_xrn)
+    (hrn, _) = urn_to_hrn(slice_xrn)
     peer = slices.get_peer(hrn)
     sfa_peer = slices.get_sfa_peer(hrn)
     slice_record=None    
@@ -198,7 +188,11 @@ def CreateSliver(api, slice_xrn, creds, rspec_string, users, call_id):
     requested_slivers = [str(host) for host in rspec.version.get_nodes_with_slivers()]
     slices.verify_slice_nodes(slice, requested_slivers, peer) 
 
-    # hanlde MyPLC peer association.
+    aggregate.prepare_nodes({'hostname': requested_slivers})
+    aggregate.prepare_interfaces({'node_id': aggregate.nodes.keys()})    
+    slices.verify_slice_links(slice, rspec.version.get_link_requests(), aggregate)
+
+    # handle MyPLC peer association.
     # only used by plc and ple.
     slices.handle_peer(site, slice, persons, peer)
     
@@ -207,7 +201,7 @@ def CreateSliver(api, slice_xrn, creds, rspec_string, users, call_id):
 
 def RenewSliver(api, xrn, creds, expiration_time, call_id):
     if Callids().already_handled(call_id): return True
-    (hrn, type) = urn_to_hrn(xrn)
+    (hrn, _) = urn_to_hrn(xrn)
     slicename = hrn_to_pl_slicename(hrn)
     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
     if not slices:
@@ -222,7 +216,7 @@ def RenewSliver(api, xrn, creds, expiration_time, call_id):
         return False
 
 def start_slice(api, xrn, creds):
-    hrn, type = urn_to_hrn(xrn)
+    (hrn, _) = urn_to_hrn(xrn)
     slicename = hrn_to_pl_slicename(hrn)
     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
     if not slices:
@@ -236,7 +230,7 @@ def start_slice(api, xrn, creds):
     return 1
  
 def stop_slice(api, xrn, creds):
-    hrn, type = urn_to_hrn(xrn)
+    hrn, _ = urn_to_hrn(xrn)
     slicename = hrn_to_pl_slicename(hrn)
     slices = api.plshell.GetSlices(api.plauth, {'name': slicename}, ['slice_id'])
     if not slices:
@@ -246,7 +240,7 @@ def stop_slice(api, xrn, creds):
     if not slice_tags:
         api.plshell.AddSliceTag(api.plauth, slice_id, 'enabled', '0')
     elif slice_tags[0]['value'] != "0":
-        tag_id = attributes[0]['slice_tag_id']
+        tag_id = slice_tags[0]['slice_tag_id']
         api.plshell.UpdateSliceTag(api.plauth, tag_id, '0')
     return 1
 
@@ -256,7 +250,7 @@ def reset_slice(api, xrn):
 
 def DeleteSliver(api, xrn, creds, call_id):
     if Callids().already_handled(call_id): return ""
-    (hrn, type) = urn_to_hrn(xrn)
+    (hrn, _) = urn_to_hrn(xrn)
     slicename = hrn_to_pl_slicename(hrn)
     slices = api.plshell.GetSlices(api.plauth, {'name': slicename})
     if not slices:
@@ -300,7 +294,7 @@ def ListResources(api, creds, options, call_id):
     if Callids().already_handled(call_id): return ""
     # get slice's hrn from options
     xrn = options.get('geni_slice_urn', None)
-    (hrn, type) = urn_to_hrn(xrn)
+    (hrn, _) = urn_to_hrn(xrn)
 
     version_manager = VersionManager()
     # get the rspec's return format from options
@@ -332,36 +326,45 @@ def ListResources(api, creds, options, call_id):
 
 def get_ticket(api, xrn, creds, rspec, users):
 
-    reg_objects = __get_registry_objects(xrn, creds, users)
-
-    slice_hrn, type = urn_to_hrn(xrn)
+    (slice_hrn, _) = urn_to_hrn(xrn)
     slices = Slices(api)
     peer = slices.get_peer(slice_hrn)
     sfa_peer = slices.get_sfa_peer(slice_hrn)
 
     # get the slice record
-    registry = api.registries[api.hrn]
     credential = api.getCredential()
+    interface = api.registries[api.hrn]
+    registry = api.get_server(interface, credential)
     records = registry.Resolve(xrn, credential)
 
-    # similar to CreateSliver, we must verify that the required records exist
-    # at this aggregate before we can issue a ticket
-    site_id, remote_site_id = slices.verify_site(registry, credential, slice_hrn,
-                                                 peer, sfa_peer, reg_objects)
-    slice = slices.verify_slice(registry, credential, slice_hrn, site_id,
-                                remote_site_id, peer, sfa_peer, reg_objects)
-
     # make sure we get a local slice record
     record = None
     for tmp_record in records:
         if tmp_record['type'] == 'slice' and \
            not tmp_record['peer_authority']:
+#Error (E0602, get_ticket): Undefined variable 'SliceRecord'
             record = SliceRecord(dict=tmp_record)
     if not record:
         raise RecordNotFound(slice_hrn)
+    
+    # similar to CreateSliver, we must verify that the required records exist
+    # at this aggregate before we can issue a ticket
+    # parse rspec
+    rspec = RSpec(rspec_string)
+    requested_attributes = rspec.version.get_slice_attributes()
 
+    # ensure site record exists
+    site = slices.verify_site(hrn, slice_record, peer, sfa_peer)
+    # ensure slice record exists
+    slice = slices.verify_slice(hrn, slice_record, peer, sfa_peer)
+    # ensure person records exists
+    persons = slices.verify_persons(hrn, slice, users, peer, sfa_peer)
+    # ensure slice attributes exists
+    slices.verify_slice_attributes(slice, requested_attributes)
+    
     # get sliver info
-    slivers = Slices(api).get_slivers(slice_hrn)
+    slivers = slices.get_slivers(slice_hrn)
+
     if not slivers:
         raise SliverDoesNotExist(slice_hrn)
 
@@ -391,16 +394,18 @@ def get_ticket(api, xrn, creds, rspec, users):
 
 
 def main():
-    api = SfaAPI()
     """
     rspec = ListResources(api, "plc.princeton.sapan", None, 'pl_test_sapan')
     #rspec = ListResources(api, "plc.princeton.coblitz", None, 'pl_test_coblitz')
     #rspec = ListResources(api, "plc.pl.sirius", None, 'pl_test_sirius')
     print rspec
     """
+    api = PlcSfaApi()
     f = open(sys.argv[1])
     xml = f.read()
     f.close()
+#Error (E1120, main): No value passed for parameter 'users' in function call
+#Error (E1120, main): No value passed for parameter 'call_id' in function call
     CreateSliver(api, "plc.princeton.sapan", xml, 'CreateSliver_sapan')
 
 if __name__ == "__main__":
index ea8f2af..6c68118 100644 (file)
@@ -15,10 +15,9 @@ from sqlobject import *
 
 from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn, Xrn
-from sfa.util.rspec import RSpec
 from sfa.server.registry import Registries
 from sfa.trust.credential import Credential
-from sfa.plc.api import SfaAPI
+from sfa.plc.plcsfaapi import PlcSfaApi
 from sfa.plc.aggregate import Aggregate
 from sfa.plc.slices import *
 from sfa.util.plxrn import hrn_to_pl_slicename, slicename_to_hrn
@@ -42,7 +41,7 @@ cloud = {}
 #
 EUCALYPTUS_RSPEC_SCHEMA='/etc/sfa/eucalyptus.rng'
 
-api = SfaAPI()
+api = PlcSfaApi()
 
 ##
 # Meta data of an instance.
@@ -736,7 +735,7 @@ def main():
 
     server_key_file = '/var/lib/sfa/authorities/server.key'
     server_cert_file = '/var/lib/sfa/authorities/server.cert'
-    api = SfaAPI(key_file = server_key_file, cert_file = server_cert_file, interface='aggregate')
+    api = PlcSfaApi(key_file = server_key_file, cert_file = server_cert_file, interface='aggregate')
     print getKeysForSlice(api, 'gc.gc.test1')
 
 if __name__ == "__main__":
index fd0e127..ff6ea73 100644 (file)
@@ -2,7 +2,6 @@ from sfa.plc.slices import Slices
 from sfa.server.registry import Registries\r
 from sfa.util.xrn import urn_to_hrn, hrn_to_urn, get_authority, Xrn\r
 from sfa.util.plxrn import hrn_to_pl_slicename\r
-from sfa.util.rspec import RSpec\r
 from sfa.util.sfalogging import logger\r
 from sfa.util.faults import *\r
 from sfa.util.config import Config\r
@@ -260,6 +259,6 @@ return the basic information needed in a dict.
 def fetch_context(slice_hrn, user_hrn, contexts):\r
     base_context = {'sfa':{'user':{'hrn':user_hrn}}}\r
     return base_context\r
-    api = SfaAPI()\r
+    api = PlcSfaApi()\r
     create_slice(api, "plc.maxpl.test000", None, rspec_xml, None)\r
 \r
index 1edc90b..a804a65 100755 (executable)
@@ -10,7 +10,6 @@ import struct
 
 from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
-from sfa.util.rspec import RSpec
 from sfa.server.registry import Registries
 from sfa.util.config import Config
 from sfa.plc.nodes import *
diff --git a/sfa/managers/aggregate_manager_vini.py b/sfa/managers/aggregate_manager_vini.py
deleted file mode 100644 (file)
index 7f36419..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-import datetime
-import time
-import traceback
-import sys
-
-from types import StringTypes
-from sfa.util.xrn import urn_to_hrn, Xrn
-from sfa.util.plxrn import hrn_to_pl_slicename
-from sfa.util.rspec import *
-from sfa.util.specdict import *
-from sfa.util.faults import *
-from sfa.util.record import SfaRecord
-from sfa.util.policy import Policy
-from sfa.util.record import *
-from sfa.util.sfaticket import SfaTicket
-from sfa.server.registry import Registries
-from sfa.plc.slices import Slices
-import sfa.plc.peers as peers
-from sfa.managers.vini.vini_network import *
-from sfa.plc.vini_aggregate import ViniAggregate
-from sfa.rspecs.version_manager import VersionManager
-from sfa.plc.api import SfaAPI
-from sfa.plc.slices import *
-from sfa.managers.aggregate_manager_pl import __get_registry_objects, __get_hostnames
-from sfa.util.version import version_core
-from sfa.util.callids import Callids
-
-# VINI aggregate is almost identical to PLC aggregate for many operations, 
-# so lets just import the methods form the PLC manager
-from sfa.managers.aggregate_manager_pl import (
-start_slice, stop_slice, RenewSliver, reset_slice, ListSlices, get_ticket, SliverStatus)
-
-
-def GetVersion(api):
-    xrn=Xrn(api.hrn)
-    return version_core({'interface':'aggregate',
-                         'testbed':'myplc.vini',
-                         'hrn':xrn.get_hrn(),
-                         })
-
-def DeleteSliver(api, xrn, creds, call_id):
-    if Callids().already_handled(call_id): return ""
-    (hrn, type) = urn_to_hrn(xrn)
-    slicename = hrn_to_pl_slicename(hrn)
-    slices = api.plshell.GetSlices(api.plauth, {'name': slicename})
-    if not slices:
-        return 1
-    slice = slices[0]
-
-    api.plshell.DeleteSliceFromNodes(api.plauth, slicename, slice['node_ids'])
-    return 1
-
-def CreateSliver(api, xrn, creds, xml, users, call_id):
-    """
-    Verify HRN and initialize the slice record in PLC if necessary.
-    """
-
-    if Callids().already_handled(call_id): return ""
-
-    hrn, type = urn_to_hrn(xrn)
-    peer = None
-    reg_objects = __get_registry_objects(xrn, creds, users)
-    slices = Slices(api)
-    peer = slices.get_peer(hrn)
-    sfa_peer = slices.get_sfa_peer(hrn)
-    registries = Registries(api)
-    registry = registries[api.hrn]
-    credential = api.getCredential()
-    site_id, remote_site_id = slices.verify_site(registry, credential, hrn, 
-                                                 peer, sfa_peer, reg_objects)
-    slice = slices.verify_slice(registry, credential, hrn, site_id, 
-                                remote_site_id, peer, sfa_peer, reg_objects)
-
-    network = ViniNetwork(api)
-
-    slice = network.get_slice(api, hrn)
-    current = __get_hostnames(slice.get_nodes())
-
-    network.addRSpec(xml, "/var/www/html/schemas/vini.rng")
-    #network.addRSpec(xml, "/root/SVN/sfa/trunk/sfa/managers/vini/vini.rng")
-    request = __get_hostnames(network.nodesWithSlivers())
-    
-    # remove nodes not in rspec
-    deleted_nodes = list(set(current).difference(request))
-
-    # add nodes from rspec
-    added_nodes = list(set(request).difference(current))
-
-    api.plshell.AddSliceToNodes(api.plauth, slice.name, added_nodes) 
-    api.plshell.DeleteSliceFromNodes(api.plauth, slice.name, deleted_nodes)
-    network.updateSliceTags()
-
-    # xxx - check this holds enough data for the client to understand what's happened
-    return network.toxml()
-
-def ListResources(api, creds, options,call_id):
-    if Callids().already_handled(call_id): return ""
-    # get slice's hrn from options
-    xrn = options.get('geni_slice_urn', '')
-    hrn, type = urn_to_hrn(xrn)
-
-    version_manager = VersionManager()
-    # get the rspec's return format from options
-    rspec_version = version_manager.get_version(options.get('rspec_version'))
-    version_string = "rspec_%s" % (rspec_version.to_string())
-    
-    # look in cache first
-    if api.cache and not xrn:
-        rspec = api.cache.get(version_string)
-        if rspec:
-            api.logger.info("aggregate.ListResources: returning cached value for hrn %s"%hrn)
-            return rspec
-
-    aggregate = ViniAggregate(api, options) 
-    rspec =  aggregate.get_rspec(slice_xrn=xrn, version=rspec_version)
-           
-    # cache the result
-    if api.cache and not xrn:
-        api.cache.add('nodes', rspec)
-
-    return rspec
-
-def main():
-    api = SfaAPI()
-    """
-    #rspec = ListResources(api, None, None,)
-    rspec = ListResources(api, "plc.princeton.iias", None, 'vini_test')
-    print rspec
-    """
-    f = open(sys.argv[1])
-    xml = f.read()
-    f.close()
-    CreateSliver(api, "plc.princeton.iias", xml, 'call-id-iias')
-
-if __name__ == "__main__":
-    main()
index 6100e76..8aca53c 100644 (file)
@@ -1,9 +1,8 @@
-import os
 import xmlrpclib
 
-from sfa.util.faults import *
+from sfa.util.faults import SliverDoesNotExist
 from sfa.util.plxrn import PlXrn
-from sfa.util.sfaticket import SfaTicket
+from sfa.trust.sfaticket import SfaTicket
 from sfa.util.version import version_core
 
 def GetVersion(api):
diff --git a/sfa/managers/managerwrapper.py b/sfa/managers/managerwrapper.py
new file mode 100644 (file)
index 0000000..5231c2a
--- /dev/null
@@ -0,0 +1,24 @@
+from sfa.util.faults import SfaNotImplemented
+from sfa.util.sfalogging import logger
+
+####################
+class ManagerWrapper:
+    """
+    This class acts as a wrapper around an SFA interface manager module, but
+    can be used with any python module. The purpose of this class is raise a 
+    SfaNotImplemented exception if someone attempts to use an attribute 
+    (could be a callable) thats not available in the library by checking the
+    library using hasattr. This helps to communicate better errors messages 
+    to the users and developers in the event that a specifiec operation 
+    is not implemented by a libarary and will generally be more helpful than
+    the standard AttributeError         
+    """
+    def __init__(self, manager, interface):
+        self.manager = manager
+        self.interface = interface
+        
+    def __getattr__(self, method):
+        if not hasattr(self.manager, method):
+            raise SfaNotImplemented(method, self.interface)
+        return getattr(self.manager, method)
+        
similarity index 96%
rename from sfa/managers/registry_manager_pl.py
rename to sfa/managers/registry_manager.py
index 6052eee..085bc39 100644 (file)
@@ -1,18 +1,19 @@
 import types
 import time 
 
-from sfa.util.faults import *
+from sfa.util.faults import RecordNotFound, AccountNotEnabled, PermissionError, MissingAuthority, \
+    UnknownSfaType, ExistingRecord
 from sfa.util.prefixTree import prefixTree
 from sfa.util.record import SfaRecord
 from sfa.util.table import SfaTable
-from sfa.util.record import SfaRecord
-from sfa.trust.gid import GID 
 from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn, urn_to_hrn
 from sfa.util.plxrn import hrn_to_pl_login_base
+from sfa.util.version import version_core
+
+from sfa.trust.gid import GID 
 from sfa.trust.credential import Credential
-from sfa.trust.certificate import Certificate, Keypair
+from sfa.trust.certificate import Certificate, Keypair, convert_public_key
 from sfa.trust.gid import create_uuid
-from sfa.util.version import version_core
 
 # The GENI GetVersion call
 def GetVersion(api):
@@ -118,7 +119,9 @@ def resolve(api, xrns, type=None, full=True):
         xrns = xrn_dict[registry_hrn]
         if registry_hrn != api.hrn:
             credential = api.getCredential()
-            peer_records = registries[registry_hrn].Resolve(xrns, credential)
+            interface = api.registries[registry_hrn]
+            server = api.get_server(interface, credential)
+            peer_records = server.Resolve(xrns, credential)
             records.extend([SfaRecord(dict=record).as_dict() for record in peer_records])
 
     # try resolving the remaining unfound records at the local registry
@@ -154,13 +157,14 @@ def list(api, xrn, origin_hrn=None):
     #if there was no match then this record belongs to an unknow registry
     if not registry_hrn:
         raise MissingAuthority(xrn)
-    
     # if the best match (longest matching hrn) is not the local registry,
     # forward the request
     records = []    
     if registry_hrn != api.hrn:
         credential = api.getCredential()
-        record_list = registries[registry_hrn].List(xrn, credential)
+        interface = api.registries[registry_hrn]
+        server = api.get_server(interface, credential)
+        record_list = server.List(xrn, credential)
         records = [SfaRecord(dict=record).as_dict() for record in record_list]
     
     # if we still have not found the record yet, try the local registry
similarity index 94%
rename from sfa/managers/slice_manager_pl.py
rename to sfa/managers/slice_manager.py
index 8d5a695..2911313 100644 (file)
@@ -1,34 +1,22 @@
-#
 import sys
-import time,datetime
+import time
 from StringIO import StringIO
-from types import StringTypes
-from copy import deepcopy
 from copy import copy
 from lxml import etree
 
-from sfa.util.sfalogging import logger
-from sfa.util.rspecHelper import merge_rspecs
-from sfa.util.xrn import Xrn, urn_to_hrn, hrn_to_urn
-from sfa.util.plxrn import hrn_to_pl_slicename
-from sfa.util.rspec import *
-from sfa.util.specdict import *
-from sfa.util.faults import *
-from sfa.util.record import SfaRecord
-from sfa.rspecs.rspec_converter import RSpecConverter
-from sfa.client.client_helper import sfa_to_pg_users_arg
-from sfa.rspecs.version_manager import VersionManager
-from sfa.rspecs.rspec import RSpec 
-from sfa.util.policy import Policy
-from sfa.util.prefixTree import prefixTree
-from sfa.util.sfaticket import *
+from sfa.trust.sfaticket import SfaTicket
 from sfa.trust.credential import Credential
+
+from sfa.util.sfalogging import logger
+from sfa.util.xrn import Xrn, urn_to_hrn
 from sfa.util.threadmanager import ThreadManager
-import sfa.util.xmlrpcprotocol as xmlrpcprotocol     
-import sfa.plc.peers as peers
 from sfa.util.version import version_core
 from sfa.util.callids import Callids
 
+from sfa.rspecs.rspec_converter import RSpecConverter
+from sfa.rspecs.version_manager import VersionManager
+from sfa.rspecs.rspec import RSpec 
+from sfa.client.client_helper import sfa_to_pg_users_arg
 
 def _call_id_supported(api, server):
     """
@@ -92,7 +80,7 @@ def drop_slicemgr_stats(rspec):
         for node in stats_elements:
             node.getparent().remove(node)
     except Exception, e:
-        api.logger.warn("drop_slicemgr_stats failed: %s " % (str(e)))
+        logger.warn("drop_slicemgr_stats failed: %s " % (str(e)))
 
 def add_slicemgr_stat(rspec, callname, aggname, elapsed, status):
     try:
@@ -104,7 +92,7 @@ def add_slicemgr_stat(rspec, callname, aggname, elapsed, status):
 
         etree.SubElement(stats_tag, "aggregate", name=str(aggname), elapsed=str(elapsed), status=str(status))
     except Exception, e:
-        api.logger.warn("add_slicemgr_stat failed on  %s: %s" %(aggname, str(e)))
+        logger.warn("add_slicemgr_stat failed on  %s: %s" %(aggname, str(e)))
 
 def ListResources(api, creds, options, call_id):
     version_manager = VersionManager()
@@ -219,9 +207,9 @@ def CreateSliver(api, xrn, creds, rspec_str, users, call_id):
     # The schema used here needs to aggregate the PL and VINI schemas
     # schema = "/var/www/html/schemas/pl.rng"
     rspec = RSpec(rspec_str)
-    schema = None
-    if schema:
-        rspec.validate(schema)
+#    schema = None
+#    if schema:
+#        rspec.validate(schema)
 
     # if there is a <statistics> section, the aggregates don't care about it,
     # so delete it.
@@ -445,7 +433,7 @@ def get_ticket(api, xrn, creds, rspec, users):
     results = threads.get_results()
     
     # gather information from each ticket 
-    rspecs = []
+    rspec = None
     initscripts = []
     slivers = [] 
     object_gid = None  
@@ -454,15 +442,17 @@ def get_ticket(api, xrn, creds, rspec, users):
         attrs = agg_ticket.get_attributes()
         if not object_gid:
             object_gid = agg_ticket.get_gid_object()
-        rspecs.append(agg_ticket.get_rspec())
+        if not rspec:
+            rspec = RSpec(agg_ticket.get_rspec())
+        else:
+            rspec.version.merge(agg_ticket.get_rspec())
         initscripts.extend(attrs.get('initscripts', [])) 
         slivers.extend(attrs.get('slivers', [])) 
     
     # merge info
     attributes = {'initscripts': initscripts,
                  'slivers': slivers}
-    merged_rspec = merge_rspecs(rspecs) 
-
+    
     # create a new ticket
     ticket = SfaTicket(subject = slice_hrn)
     ticket.set_gid_caller(api.auth.client_gid)
@@ -471,7 +461,7 @@ def get_ticket(api, xrn, creds, rspec, users):
     ticket.set_pubkey(object_gid.get_pubkey())
     #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
     ticket.set_attributes(attributes)
-    ticket.set_rspec(merged_rspec)
+    ticket.set_rspec(rspec.toxml())
     ticket.encode()
     ticket.sign()          
     return ticket.save_to_string(save_parents=True)
@@ -540,11 +530,12 @@ def status(api, xrn, creds):
     """
     return 1
 
-def main():
-    r = RSpec()
-    r.parseFile(sys.argv[1])
-    rspec = r.toDict()
-    CreateSliver(None,'plc.princeton.tmacktestslice',rspec,'create-slice-tmacktestslice')
+# this is plain broken
+#def main():
+#    r = RSpec()
+#    r.parseFile(sys.argv[1])
+#    rspec = r.toDict()
+#    CreateSliver(None,'plc.princeton.tmacktestslice',rspec,'create-slice-tmacktestslice')
 
 if __name__ == "__main__":
     main()
index b905c2f..7ec3e95 100644 (file)
@@ -1,8 +1,4 @@
 #!/usr/bin/python
-
-# $Id: topology.py 14181 2009-07-01 19:46:07Z acb $
-# $URL: https://svn.planet-lab.org/svn/NodeManager-topo/trunk/topology.py $
-
 #
 # Links in the physical topology, gleaned from looking at the Internet2
 # topology map.  Link (a, b) connects sites with IDs a and b.
index 7c1bf8a..b25fbd1 100644 (file)
@@ -1,5 +1,3 @@
-### $Id: register.py 16477 2010-01-05 16:31:37Z thierry $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/register.py $
 
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
index 7895de3..bb0051a 100644 (file)
@@ -1,9 +1,10 @@
-from sfa.util.faults import *
+from sfa.util.faults import SfaInvalidArgument
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
 from sfa.util.sfatablesRuntime import run_sfatables
 from sfa.trust.credential import Credential
+from sfa.rspecs.rspec import RSpec
 
 class CreateSliver(Method):
     """
@@ -51,5 +52,9 @@ class CreateSliver(Method):
             chain_name = 'FORWARD-INCOMING'
         self.api.logger.debug("CreateSliver: sfatables on chain %s"%chain_name)
         rspec = run_sfatables(chain_name, hrn, origin_hrn, rspec)
-
-        return manager.CreateSliver(self.api, slice_xrn, creds, rspec, users, call_id)
+        slivers = RSpec(rspec).version.get_nodes_with_slivers()
+        if slivers:
+            result = manager.CreateSliver(self.api, slice_xrn, creds, rspec, users, call_id)
+        else:
+            result = rspec     
+        return result
index ae30177..f766cb1 100644 (file)
@@ -1,4 +1,3 @@
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
index 34a4cb9..da3e97b 100644 (file)
@@ -1,6 +1,3 @@
-#
-from sfa.trust.rights import *
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
index 37ad796..e50f940 100644 (file)
@@ -1,9 +1,6 @@
-from sfa.util.faults import *
+from sfa.util.faults import RecordNotFound
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
-from sfa.trust.gid import GID
-from sfa.trust.certificate import Certificate
 from sfa.trust.credential import Credential
 
 class GetGids(Method):
@@ -30,15 +27,15 @@ class GetGids(Method):
     def call(self, xrns, creds):
         # validate the credential
         valid_creds = self.api.auth.checkCredentials(creds, 'getgids')
+        # xxxpylintxxx origin_hrn is unused..
         origin_hrn = Credential(string=valid_creds[0]).get_gid_caller().get_hrn()
         
         # resolve the record
         manager = self.api.get_interface_manager()
         records = manager.resolve(self.api, xrns, full = False)
         if not records:
-            raise RecordNotFound(hrns)
+            raise RecordNotFound(xrns)
 
-        gids = []
         allowed_fields =  ['hrn', 'type', 'gid']
         for record in records:
             for key in record.keys():
index 6a8261c..de21ab5 100644 (file)
@@ -1,12 +1,10 @@
 
-from sfa.util.faults import *
+from sfa.util.faults import RecordNotFound, ConnectionKeyGIDMismatch
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
 from sfa.util.record import SfaRecord
-from sfa.trust.credential import Credential
 from sfa.trust.certificate import Certificate
-from sfa.trust.rights import Right, Rights
 
 class GetSelfCredential(Method):
     """
index e175cfe..1469693 100644 (file)
@@ -1,15 +1,10 @@
-### $Id: get_ticket.py 17732 2010-04-19 21:10:45Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/get_ticket.py $
-import time
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
-from sfa.util.config import Config
-from sfa.trust.credential import Credential
 from sfa.util.sfatablesRuntime import run_sfatables
 
+from sfa.trust.credential import Credential
+
 class GetTicket(Method):
     """
     Retrieve a ticket. This operation is currently implemented on PLC
index 2a8f6b2..72fe806 100644 (file)
@@ -1,4 +1,3 @@
-from sfa.util.faults import *
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter
 
index 8b4fcbe..a5d1123 100644 (file)
@@ -1,5 +1,4 @@
 
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
index fb83117..b8d7e2d 100644 (file)
@@ -1,7 +1,5 @@
-import sys
 import zlib
 
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
index fa65b07..7fc8513 100644 (file)
@@ -1,7 +1,5 @@
-from sfa.util.faults import *
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
 from sfa.trust.credential import Credential
  
 class ListSlices(Method):
index f66e90c..cab0e93 100644 (file)
@@ -1,7 +1,3 @@
-### $Id: reset_slice.py 15428 2009-10-23 15:28:03Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfacomponent/methods/reset_slice.py $
-import xmlrpclib
-from sfa.util.faults import *
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
 
index 6f61870..619ed00 100644 (file)
@@ -1,14 +1,5 @@
-### $Id: register.py 16477 2010-01-05 16:31:37Z thierry $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/register.py $
-
-from sfa.trust.certificate import Keypair, convert_public_key
-from sfa.trust.gid import *
-from sfa.util.faults import *
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.util.record import SfaRecord
-from sfa.trust.auth import Auth
-from sfa.trust.gid import create_uuid
 from sfa.trust.credential import Credential
 
 class Register(Method):
index 864b1d5..2eec2f5 100644 (file)
@@ -1,17 +1,9 @@
-### $Id: register.py 15001 2009-09-11 20:18:54Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/register.py $
-
-from sfa.trust.certificate import Keypair, convert_public_key
-from sfa.trust.gid import *
-
-from sfa.util.faults import *
+from sfa.util.faults import SfaInvalidArgument
 from sfa.util.xrn import get_authority
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
 from sfa.util.record import SfaRecord
 from sfa.util.table import SfaTable
-from sfa.trust.auth import Auth
-from sfa.trust.gid import create_uuid
 from sfa.trust.credential import Credential
 
 class RegisterPeerObject(Method):
index 73437a3..c547c26 100644 (file)
@@ -1,4 +1,3 @@
-from sfa.util.faults import *
 from sfa.util.xrn import Xrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
index 460aa98..fa30e88 100644 (file)
@@ -1,11 +1,8 @@
-from sfa.util.faults import *
+from sfa.util.faults import UnknownSfaType, SfaInvalidArgument
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
-from sfa.util.record import SfaRecord
 from sfa.util.table import SfaTable
 from sfa.trust.credential import Credential
-from types import StringTypes
 
 class RemovePeerObject(Method):
     """
index 1669517..4a0e836 100644 (file)
@@ -1,10 +1,11 @@
-from sfa.util.faults import *
+import datetime
+
+from sfa.util.faults import InsufficientRights
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter
 from sfa.trust.credential import Credential
 from sfa.util.sfatime import utcparse
-import datetime
 
 class RenewSliver(Method):
     """
index 49104b2..74972cc 100644 (file)
@@ -1,8 +1,5 @@
-### $Id: resolve.py 17157 2010-02-21 04:19:34Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/resolve.py $
-import traceback
 import types
-from sfa.util.faults import *
+
 from sfa.util.xrn import Xrn, urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
@@ -45,6 +42,4 @@ class Resolve(Method):
         # send the call to the right manager
         manager = self.api.get_interface_manager()
         return manager.resolve(self.api, xrns, type)
-
-
             
index c6e5272..d978197 100644 (file)
@@ -1,7 +1,5 @@
-from sfa.util.faults import *
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter
-from sfa.trust.credential import Credential
 
 class ResolveGENI(Method):
     """
index 00142b6..9788608 100644 (file)
@@ -1,5 +1,3 @@
-from sfa.util.faults import *
-from sfa.util.method import Method
 from sfa.util.parameter import Parameter
 from sfa.methods.Stop import Stop
 
index 231bec5..18613b2 100644 (file)
@@ -1,4 +1,3 @@
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
index e1ca60e..6882a37 100644 (file)
@@ -1,11 +1,6 @@
-### $Id: stop_slice.py 17732 2010-04-19 21:10:45Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/stop_slice.py $
-
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
 from sfa.trust.credential import Credential
 
 class Start(Method):
index 579a77d..e8d3397 100644 (file)
@@ -1,11 +1,6 @@
-### $Id: stop_slice.py 17732 2010-04-19 21:10:45Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/stop_slice.py $
-
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
 from sfa.trust.credential import Credential
  
 class Stop(Method):
index aa881ea..31b17e9 100644 (file)
@@ -1,7 +1,5 @@
-import time
-from sfa.util.faults import *
 from sfa.util.method import Method
-from sfa.util.parameter import Parameter, Mixed
+from sfa.util.parameter import Parameter
 from sfa.trust.credential import Credential
 
 class Update(Method):
index 8307572..f9baae4 100644 (file)
@@ -1,5 +1,3 @@
-from sfa.util.faults import *
-from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
 from sfa.methods.CreateSliver import CreateSliver
 
index 59d6001..23c8d60 100644 (file)
@@ -1,9 +1,6 @@
-from types import StringTypes
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
 from sfa.server.aggregate import Aggregates
 
 class get_aggregates(Method):
index 9cec0ec..4bb6587 100644 (file)
@@ -1,11 +1,10 @@
 import os
 import tempfile
 import commands
-from sfa.util.faults import *
+from sfa.util.faults import NonExistingRecord, RecordNotFound
 from sfa.util.xrn import hrn_to_urn
 from sfa.util.method import Method
-from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
+from sfa.util.parameter import Parameter
 from sfa.util.table import SfaTable
 from sfa.trust.certificate import Keypair
 from sfa.trust.gid import create_uuid
index b404bb9..65d9444 100644 (file)
@@ -1,9 +1,6 @@
-from types import StringTypes
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
 from sfa.server.registry import Registries
 
 class get_registries(Method):
@@ -28,7 +25,7 @@ class get_registries(Method):
     def call(self, cred, xrn = None):
         hrn, type = urn_to_hrn(xrn)
         self.api.auth.check(cred, 'list')
-        registries = Registries(self.api).interfaces.values()
+        registries = Registries(self.api).values()
         if hrn:
             registries = [reg for reg in registries if reg['hrn'] == hrn] 
         return registries
index 704fd42..460ab4d 100644 (file)
@@ -1,4 +1,4 @@
-from sfa.util.faults import *
+#from sfa.util.faults import *
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
 from sfa.trust.auth import Auth
index e89f18b..42ef240 100644 (file)
@@ -1,17 +1,10 @@
-### $Id: register.py 15001 2009-09-11 20:18:54Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/register.py $
 
-from sfa.trust.certificate import Keypair, convert_public_key
-from sfa.trust.gid import *
-
-from sfa.util.faults import *
+from sfa.util.faults import SfaInvalidArgument
 from sfa.util.xrn import get_authority
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
 from sfa.util.record import SfaRecord
 from sfa.util.table import SfaTable
-from sfa.trust.auth import Auth
-from sfa.trust.gid import create_uuid
 from sfa.trust.credential import Credential
 
 class register_peer_object(Method):
index 41d74dc..a510189 100644 (file)
@@ -1,11 +1,8 @@
-from sfa.util.faults import *
+from sfa.util.faults import UnknownSfaType, SfaInvalidArgument
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
-from sfa.util.record import SfaRecord
 from sfa.util.table import SfaTable
 from sfa.trust.credential import Credential
-from types import StringTypes
 
 class remove_peer_object(Method):
     """
index 9d02364..15fb4a5 100644 (file)
@@ -1,12 +1,6 @@
-### $Id: reset_slices.py 15428 2009-10-23 15:28:03Z tmack $
-### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/reset_slices.py $
-
-from sfa.util.faults import *
 from sfa.util.xrn import urn_to_hrn
 from sfa.util.method import Method
 from sfa.util.parameter import Parameter, Mixed
-from sfa.trust.auth import Auth
-from sfa.plc.slices import Slices
 
 class reset_slice(Method):
     """
index 83f9324..4c398f9 100644 (file)
@@ -1,12 +1,14 @@
 #!/usr/bin/python
-from sfa.util.xrn import *
-from sfa.util.plxrn import *
-#from sfa.rspecs.sfa_rspec import SfaRSpec
-#from sfa.rspecs.pg_rspec  import PGRSpec
-#from sfa.rspecs.rspec_version import RSpecVersion
+from sfa.util.xrn import hrn_to_urn, urn_to_hrn
+from sfa.util.plxrn import PlXrn, hostname_to_urn, hrn_to_pl_slicename
+
 from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.elements.link import Link
+from sfa.rspecs.elements.interface import Interface
+
+from sfa.util.topology import Topology
 from sfa.rspecs.version_manager import VersionManager
-from sfa.util.bwlimit import get_tc_rate
+from sfa.plc.vlink import get_tc_rate
 
 class Aggregate:
 
@@ -25,14 +27,26 @@ class Aggregate:
         self.api = api
         self.user_options = user_options
 
-    def prepare_sites(self, force=False):
+    def prepare_sites(self, filter={}, force=False):
         if not self.sites or force:  
-            for site in self.api.plshell.GetSites(self.api.plauth):
+            for site in self.api.plshell.GetSites(self.api.plauth, filter):
                 self.sites[site['site_id']] = site
     
-    def prepare_nodes(self, force=False):
+    def prepare_nodes(self, filter={}, force=False):
         if not self.nodes or force:
-            for node in self.api.plshell.GetNodes(self.api.plauth, {'peer_id': None}):
+            filter.update({'peer_id': None})
+            nodes = self.api.plshell.GetNodes(self.api.plauth, filter)
+            site_ids = []
+            interface_ids = []
+            tag_ids = []
+            for node in nodes:
+                site_ids.append(node['site_id'])
+                interface_ids.extend(node['interface_ids'])
+                tag_ids.extend(node['node_tag_ids'])
+            self.prepare_sites({'site_id': site_ids})
+            self.prepare_interfaces({'interface_id': interface_ids})
+            self.prepare_node_tags({'node_tag_id': tag_ids}) 
+            for node in nodes:
                 # add site/interface info to nodes.
                 # assumes that sites, interfaces and tags have already been prepared.
                 site = self.sites[node['site_id']]
@@ -47,37 +61,80 @@ class Aggregate:
                 node['tags'] = tags
                 self.nodes[node['node_id']] = node
 
-    def prepare_interfaces(self, force=False):
+    def prepare_interfaces(self, filter={}, force=False):
         if not self.interfaces or force:
-            for interface in self.api.plshell.GetInterfaces(self.api.plauth):
+            for interface in self.api.plshell.GetInterfaces(self.api.plauth, filter):
                 self.interfaces[interface['interface_id']] = interface
 
-    def prepare_links(self, force=False):
+    def prepare_links(self, filter={}, force=False):
         if not self.links or force:
-            pass
-
-    def prepare_node_tags(self, force=False):
+            if not self.api.config.SFA_AGGREGATE_TYPE.lower() == 'vini':
+                return
+
+            topology = Topology() 
+            for (site_id1, site_id2) in topology:
+                link = Link()
+                if not site_id1 in self.sites or site_id2 not in self.sites:
+                    continue
+                site1 = self.sites[site_id1]
+                site2 = self.sites[site_id2]
+                # get hrns
+                site1_hrn = self.api.hrn + '.' + site1['login_base']
+                site2_hrn = self.api.hrn + '.' + site2['login_base']
+                # get the first node
+                node1 = self.nodes[site1['node_ids'][0]]
+                node2 = self.nodes[site2['node_ids'][0]]
+
+                # set interfaces
+                # just get first interface of the first node
+                if1_xrn = PlXrn(auth=self.api.hrn, interface='node%s:eth0' % (node1['node_id']))
+                if1_ipv4 = self.interfaces[node1['interface_ids'][0]]['ip']
+                if2_xrn = PlXrn(auth=self.api.hrn, interface='node%s:eth0' % (node2['node_id']))
+                if2_ipv4 = self.interfaces[node2['interface_ids'][0]]['ip']
+
+                if1 = Interface({'component_id': if1_xrn.urn, 'ipv4': if1_ipv4} )
+                if2 = Interface({'component_id': if2_xrn.urn, 'ipv4': if2_ipv4} )
+
+                # set link
+                link = Link({'capacity': '1000000', 'latency': '0', 'packet_loss': '0', 'type': 'ipv4'})
+                link['interface1'] = if1
+                link['interface2'] = if2
+                link['component_name'] = "%s:%s" % (site1['login_base'], site2['login_base'])
+                link['component_id'] = PlXrn(auth=self.api.hrn, interface=link['component_name']).get_urn()
+                link['component_manager_id'] =  hrn_to_urn(self.api.hrn, 'authority+am')
+                self.links[link['component_name']] = link
+
+
+    def prepare_node_tags(self, filter={}, force=False):
         if not self.node_tags or force:
-            for node_tag in self.api.plshell.GetNodeTags(self.api.plauth):
+            for node_tag in self.api.plshell.GetNodeTags(self.api.plauth, filter):
                 self.node_tags[node_tag['node_tag_id']] = node_tag
 
-    def prepare_pl_initscripts(self, force=False):
+    def prepare_pl_initscripts(self, filter={}, force=False):
         if not self.pl_initscripts or force:
-            for initscript in self.api.plshell.GetInitScripts(self.api.plauth, {'enabled': True}):
+            filter.update({'enabled': True})
+            for initscript in self.api.plshell.GetInitScripts(self.api.plauth, filter):
                 self.pl_initscripts[initscript['initscript_id']] = initscript
 
-    def prepare(self, force=False):
-        if not self.prepared or force:
-            self.prepare_sites(force)
-            self.prepare_interfaces(force)
-            self.prepare_node_tags(force)
-            self.prepare_nodes(force)
-            self.prepare_links(force)
-            self.prepare_pl_initscripts()
-        self.prepared = True  
+    def prepare(self, slice = None, force=False):
+        if not self.prepared or force or slice:
+            if not slice:
+                self.prepare_sites(force=force)
+                self.prepare_interfaces(force=force)
+                self.prepare_node_tags(force=force)
+                self.prepare_nodes(force=force)
+                self.prepare_links(force=force)
+                self.prepare_pl_initscripts(force=force)
+            else:
+                self.prepare_sites({'site_id': slice['site_id']})
+                self.prepare_interfaces({'node_id': slice['node_ids']})
+                self.prepare_node_tags({'node_id': slice['node_ids']})
+                self.prepare_nodes({'node_id': slice['node_ids']})
+                self.prepare_links({'slice_id': slice['slice_id']})
+                self.prepare_pl_initscripts()
+            self.prepared = True  
 
     def get_rspec(self, slice_xrn=None, version = None):
-        self.prepare()
         version_manager = VersionManager()
         version = version_manager.get_version(version)
         if not slice_xrn:
@@ -93,8 +150,11 @@ class Aggregate:
             slice_name = hrn_to_pl_slicename(slice_hrn)
             slices = self.api.plshell.GetSlices(self.api.plauth, slice_name)
             if slices:
-                slice = slices[0]            
-
+                slice = slices[0]
+            self.prepare(slice=slice)
+        else:
+            self.prepare()
+            
         # filter out nodes with a whitelist:
         valid_nodes = [] 
         for node in self.nodes.values():
diff --git a/sfa/plc/api.py b/sfa/plc/api.py
deleted file mode 100644 (file)
index 9891410..0000000
+++ /dev/null
@@ -1,714 +0,0 @@
-#
-# SFA XML-RPC and SOAP interfaces
-#
-
-import sys
-import os
-import traceback
-import string
-import datetime
-import xmlrpclib
-
-from sfa.util.faults import *
-from sfa.util.api import *
-from sfa.util.config import *
-from sfa.util.sfalogging import logger
-import sfa.util.xmlrpcprotocol as xmlrpcprotocol
-from sfa.trust.auth import Auth
-from sfa.trust.rights import Right, Rights, determine_rights
-from sfa.trust.credential import Credential,Keypair
-from sfa.trust.certificate import Certificate
-from sfa.util.xrn import get_authority, hrn_to_urn
-from sfa.util.plxrn import hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_slicename, slicename_to_hrn
-from sfa.util.nodemanager import NodeManager
-try:
-    from collections import defaultdict
-except:
-    class defaultdict(dict):
-        def __init__(self, default_factory=None, *a, **kw):
-            if (default_factory is not None and
-                not hasattr(default_factory, '__call__')):
-                raise TypeError('first argument must be callable')
-            dict.__init__(self, *a, **kw)
-            self.default_factory = default_factory
-        def __getitem__(self, key):
-            try:
-                return dict.__getitem__(self, key)
-            except KeyError:
-                return self.__missing__(key)
-        def __missing__(self, key):
-            if self.default_factory is None:
-                raise KeyError(key)
-            self[key] = value = self.default_factory()
-            return value
-        def __reduce__(self):
-            if self.default_factory is None:
-                args = tuple()
-            else:
-                args = self.default_factory,
-            return type(self), args, None, None, self.items()
-        def copy(self):
-            return self.__copy__()
-        def __copy__(self):
-            return type(self)(self.default_factory, self)
-        def __deepcopy__(self, memo):
-            import copy
-            return type(self)(self.default_factory,
-                              copy.deepcopy(self.items()))
-        def __repr__(self):
-            return 'defaultdict(%s, %s)' % (self.default_factory,
-                                            dict.__repr__(self))
-## end of http://code.activestate.com/recipes/523034/ }}}
-
-def list_to_dict(recs, key):
-    """
-    convert a list of dictionaries into a dictionary keyed on the 
-    specified dictionary key 
-    """
-    keys = [rec[key] for rec in recs]
-    return dict(zip(keys, recs))
-
-class SfaAPI(BaseAPI):
-
-    # flat list of method names
-    import sfa.methods
-    methods = sfa.methods.all
-    
-    def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8", 
-                 methods='sfa.methods', peer_cert = None, interface = None, 
-                key_file = None, cert_file = None, cache = None):
-        BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, \
-                         peer_cert=peer_cert, interface=interface, key_file=key_file, \
-                         cert_file=cert_file, cache=cache)
-        self.encoding = encoding
-        from sfa.util.table import SfaTable
-        self.SfaTable = SfaTable
-        # Better just be documenting the API
-        if config is None:
-            return
-
-        # Load configuration
-        self.config = Config(config)
-        self.auth = Auth(peer_cert)
-        self.interface = interface
-        self.key_file = key_file
-        self.key = Keypair(filename=self.key_file)
-        self.cert_file = cert_file
-        self.cert = Certificate(filename=self.cert_file)
-        self.credential = None
-        # Initialize the PLC shell only if SFA wraps a myPLC
-        rspec_type = self.config.get_aggregate_type()
-        if (rspec_type == 'pl' or rspec_type == 'vini' or \
-            rspec_type == 'eucalyptus' or rspec_type == 'max'):
-            self.plshell = self.getPLCShell()
-            self.plshell_version = "4.3"
-
-        self.hrn = self.config.SFA_INTERFACE_HRN
-        self.time_format = "%Y-%m-%d %H:%M:%S"
-
-    
-    def getPLCShell(self):
-        self.plauth = {'Username': self.config.SFA_PLC_USER,
-                       'AuthMethod': 'password',
-                       'AuthString': self.config.SFA_PLC_PASSWORD}
-
-        # The native shell (PLC.Shell.Shell) is more efficient than xmlrpc,
-        # but it leaves idle db connections open. use xmlrpc until we can figure
-        # out why PLC.Shell.Shell doesn't close db connection properly     
-        #try:
-        #    sys.path.append(os.path.dirname(os.path.realpath("/usr/bin/plcsh")))
-        #    self.plshell_type = 'direct'
-        #    import PLC.Shell
-        #    shell = PLC.Shell.Shell(globals = globals())
-        #except:
-        
-        self.plshell_type = 'xmlrpc' 
-        url = self.config.SFA_PLC_URL
-        shell = xmlrpclib.Server(url, verbose = 0, allow_none = True)
-        return shell
-
-    def get_server(self, interface, cred, timeout=30):
-        """
-        Returns a connection to the specified interface. Use the specified
-        credential to determine the caller and look for the caller's key/cert 
-        in the registry hierarchy cache. 
-        """       
-        from sfa.trust.hierarchy import Hierarchy
-        if not isinstance(cred, Credential):
-            cred_obj = Credential(string=cred)
-        else:
-            cred_obj = cred
-        caller_gid = cred_obj.get_gid_caller()
-        hierarchy = Hierarchy()
-        auth_info = hierarchy.get_auth_info(caller_gid.get_hrn())
-        key_file = auth_info.get_privkey_filename()
-        cert_file = auth_info.get_gid_filename()
-        server = interface.get_server(key_file, cert_file, timeout)
-        return server
-               
-        
-    def getCredential(self):
-        """
-        Return a valid credential for this interface. 
-        """
-        type = 'authority'
-        path = self.config.SFA_DATA_DIR
-        filename = ".".join([self.interface, self.hrn, type, "cred"])
-        cred_filename = path + os.sep + filename
-        cred = None
-        if os.path.isfile(cred_filename):
-            cred = Credential(filename = cred_filename)
-            # make sure cred isnt expired
-            if not cred.get_expiration or \
-               datetime.datetime.utcnow() < cred.get_expiration():    
-                return cred.save_to_string(save_parents=True)
-
-        # get a new credential
-        if self.interface in ['registry']:
-            cred =  self.__getCredentialRaw()
-        else:
-            cred =  self.__getCredential()
-        cred.save_to_file(cred_filename, save_parents=True)
-
-        return cred.save_to_string(save_parents=True)
-
-
-    def getDelegatedCredential(self, creds):
-        """
-        Attempt to find a credential delegated to us in
-        the specified list of creds.
-        """
-        if creds and not isinstance(creds, list): 
-            creds = [creds]
-        delegated_creds = filter_creds_by_caller(creds, [self.hrn, self.hrn + '.slicemanager'])
-        if not delegated_creds:
-            return None
-        return delegated_creds[0]
-    def __getCredential(self):
-        """ 
-        Get our credential from a remote registry 
-        """
-        from sfa.server.registry import Registries
-        registries = Registries()
-        registry = registries.get_server(self.hrn, self.key_file, self.cert_file)
-        cert_string=self.cert.save_to_string(save_parents=True)
-        # get self credential
-        self_cred = registry.GetSelfCredential(cert_string, self.hrn, 'authority')
-        # get credential
-        cred = registry.GetCredential(self_cred, self.hrn, 'authority')
-        return Credential(string=cred)
-
-    def __getCredentialRaw(self):
-        """
-        Get our current credential directly from the local registry.
-        """
-
-        hrn = self.hrn
-        auth_hrn = self.auth.get_authority(hrn)
-    
-        # is this a root or sub authority
-        if not auth_hrn or hrn == self.config.SFA_INTERFACE_HRN:
-            auth_hrn = hrn
-        auth_info = self.auth.get_auth_info(auth_hrn)
-        table = self.SfaTable()
-        records = table.findObjects({'hrn': hrn, 'type': 'authority+sa'})
-        if not records:
-            raise RecordNotFound
-        record = records[0]
-        type = record['type']
-        object_gid = record.get_gid_object()
-        new_cred = Credential(subject = object_gid.get_subject())
-        new_cred.set_gid_caller(object_gid)
-        new_cred.set_gid_object(object_gid)
-        new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
-        
-        r1 = determine_rights(type, hrn)
-        new_cred.set_privileges(r1)
-        new_cred.encode()
-        new_cred.sign()
-
-        return new_cred
-   
-
-    def loadCredential (self):
-        """
-        Attempt to load credential from file if it exists. If it doesnt get
-        credential from registry.
-        """
-
-        # see if this file exists
-        # XX This is really the aggregate's credential. Using this is easier than getting
-        # the registry's credential from iteslf (ssl errors).   
-        ma_cred_filename = self.config.SFA_DATA_DIR + os.sep + self.interface + self.hrn + ".ma.cred"
-        try:
-            self.credential = Credential(filename = ma_cred_filename)
-        except IOError:
-            self.credential = self.getCredentialFromRegistry()
-
-
-
-    ##
-    # Convert SFA fields to PLC fields for use when registering up updating
-    # registry record in the PLC database
-    #
-    # @param type type of record (user, slice, ...)
-    # @param hrn human readable name
-    # @param sfa_fields dictionary of SFA fields
-    # @param pl_fields dictionary of PLC fields (output)
-
-    def sfa_fields_to_pl_fields(self, type, hrn, record):
-
-        def convert_ints(tmpdict, int_fields):
-            for field in int_fields:
-                if field in tmpdict:
-                    tmpdict[field] = int(tmpdict[field])
-
-        pl_record = {}
-        #for field in record:
-        #    pl_record[field] = record[field]
-        if type == "slice":
-            if not "instantiation" in pl_record:
-                pl_record["instantiation"] = "plc-instantiated"
-            pl_record["name"] = hrn_to_pl_slicename(hrn)
-           if "url" in record:
-               pl_record["url"] = record["url"]
-           if "description" in record:
-               pl_record["description"] = record["description"]
-           if "expires" in record:
-               pl_record["expires"] = int(record["expires"])
-
-        elif type == "node":
-            if not "hostname" in pl_record:
-                if not "hostname" in record:
-                    raise MissingSfaInfo("hostname")
-                pl_record["hostname"] = record["hostname"]
-            if not "model" in pl_record:
-                pl_record["model"] = "geni"
-
-        elif type == "authority":
-            pl_record["login_base"] = hrn_to_pl_login_base(hrn)
-
-            if not "name" in pl_record:
-                pl_record["name"] = hrn
-
-            if not "abbreviated_name" in pl_record:
-                pl_record["abbreviated_name"] = hrn
-
-            if not "enabled" in pl_record:
-                pl_record["enabled"] = True
-
-            if not "is_public" in pl_record:
-                pl_record["is_public"] = True
-
-        return pl_record
-
-    def fill_record_pl_info(self, records):
-        """
-        Fill in the planetlab specific fields of a SFA record. This
-        involves calling the appropriate PLC method to retrieve the 
-        database record for the object.
-        
-        PLC data is filled into the pl_info field of the record.
-    
-        @param record: record to fill in field (in/out param)     
-        """
-        # get ids by type
-        node_ids, site_ids, slice_ids = [], [], [] 
-        person_ids, key_ids = [], []
-        type_map = {'node': node_ids, 'authority': site_ids,
-                    'slice': slice_ids, 'user': person_ids}
-                  
-        for record in records:
-            for type in type_map:
-                if type == record['type']:
-                    type_map[type].append(record['pointer'])
-
-        # get pl records
-        nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
-        if node_ids:
-            node_list = self.plshell.GetNodes(self.plauth, node_ids)
-            nodes = list_to_dict(node_list, 'node_id')
-        if site_ids:
-            site_list = self.plshell.GetSites(self.plauth, site_ids)
-            sites = list_to_dict(site_list, 'site_id')
-        if slice_ids:
-            slice_list = self.plshell.GetSlices(self.plauth, slice_ids)
-            slices = list_to_dict(slice_list, 'slice_id')
-        if person_ids:
-            person_list = self.plshell.GetPersons(self.plauth, person_ids)
-            persons = list_to_dict(person_list, 'person_id')
-            for person in persons:
-                key_ids.extend(persons[person]['key_ids'])
-
-        pl_records = {'node': nodes, 'authority': sites,
-                      'slice': slices, 'user': persons}
-
-        if key_ids:
-            key_list = self.plshell.GetKeys(self.plauth, key_ids)
-            keys = list_to_dict(key_list, 'key_id')
-
-        # fill record info
-        for record in records:
-            # records with pointer==-1 do not have plc info.
-            # for example, the top level authority records which are
-            # authorities, but not PL "sites"
-            if record['pointer'] == -1:
-                continue
-           
-            for type in pl_records:
-                if record['type'] == type:
-                    if record['pointer'] in pl_records[type]:
-                        record.update(pl_records[type][record['pointer']])
-                        break
-            # fill in key info
-            if record['type'] == 'user':
-                if 'key_ids' not in record:
-                    logger.info("user record has no 'key_ids' - need to import from myplc ?")
-                else:
-                    pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys] 
-                    record['keys'] = pubkeys
-
-        # fill in record hrns
-        records = self.fill_record_hrns(records)   
-        return records
-
-    def fill_record_hrns(self, records):
-        """
-        convert pl ids to hrns
-        """
-
-        # get ids
-        slice_ids, person_ids, site_ids, node_ids = [], [], [], []
-        for record in records:
-            if 'site_id' in record:
-                site_ids.append(record['site_id'])
-            if 'site_ids' in records:
-                site_ids.extend(record['site_ids'])
-            if 'person_ids' in record:
-                person_ids.extend(record['person_ids'])
-            if 'slice_ids' in record:
-                slice_ids.extend(record['slice_ids'])
-            if 'node_ids' in record:
-                node_ids.extend(record['node_ids'])
-
-        # get pl records
-        slices, persons, sites, nodes = {}, {}, {}, {}
-        if site_ids:
-            site_list = self.plshell.GetSites(self.plauth, site_ids, ['site_id', 'login_base'])
-            sites = list_to_dict(site_list, 'site_id')
-        if person_ids:
-            person_list = self.plshell.GetPersons(self.plauth, person_ids, ['person_id', 'email'])
-            persons = list_to_dict(person_list, 'person_id')
-        if slice_ids:
-            slice_list = self.plshell.GetSlices(self.plauth, slice_ids, ['slice_id', 'name'])
-            slices = list_to_dict(slice_list, 'slice_id')       
-        if node_ids:
-            node_list = self.plshell.GetNodes(self.plauth, node_ids, ['node_id', 'hostname'])
-            nodes = list_to_dict(node_list, 'node_id')
-       
-        # convert ids to hrns
-        for record in records:
-            # get all relevant data
-            type = record['type']
-            pointer = record['pointer']
-            auth_hrn = self.hrn
-            login_base = ''
-            if pointer == -1:
-                continue
-
-            if 'site_id' in record:
-                site = sites[record['site_id']]
-                login_base = site['login_base']
-                record['site'] = ".".join([auth_hrn, login_base])
-            if 'person_ids' in record:
-                emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
-                          if person_id in  persons]
-                usernames = [email.split('@')[0] for email in emails]
-                person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
-                record['persons'] = person_hrns 
-            if 'slice_ids' in record:
-                slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
-                              if slice_id in slices]
-                slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
-                record['slices'] = slice_hrns
-            if 'node_ids' in record:
-                hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
-                             if node_id in nodes]
-                node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
-                record['nodes'] = node_hrns
-            if 'site_ids' in record:
-                login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
-                               if site_id in sites]
-                site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
-                record['sites'] = site_hrns
-            
-        return records   
-
-    def fill_record_sfa_info(self, records):
-
-        def startswith(prefix, values):
-            return [value for value in values if value.startswith(prefix)]
-
-        # get person ids
-        person_ids = []
-        site_ids = []
-        for record in records:
-            person_ids.extend(record.get("person_ids", []))
-            site_ids.extend(record.get("site_ids", [])) 
-            if 'site_id' in record:
-                site_ids.append(record['site_id']) 
-        
-        # get all pis from the sites we've encountered
-        # and store them in a dictionary keyed on site_id 
-        site_pis = {}
-        if site_ids:
-            pi_filter = {'|roles': ['pi'], '|site_ids': site_ids} 
-            pi_list = self.plshell.GetPersons(self.plauth, pi_filter, ['person_id', 'site_ids'])
-            for pi in pi_list:
-                # we will need the pi's hrns also
-                person_ids.append(pi['person_id'])
-                
-                # we also need to keep track of the sites these pis
-                # belong to
-                for site_id in pi['site_ids']:
-                    if site_id in site_pis:
-                        site_pis[site_id].append(pi)
-                    else:
-                        site_pis[site_id] = [pi]
-                 
-        # get sfa records for all records associated with these records.   
-        # we'll replace pl ids (person_ids) with hrns from the sfa records
-        # we obtain
-        
-        # get the sfa records
-        table = self.SfaTable()
-        person_list, persons = [], {}
-        person_list = table.find({'type': 'user', 'pointer': person_ids})
-        # create a hrns keyed on the sfa record's pointer.
-        # Its possible for  multiple records to have the same pointer so
-        # the dict's value will be a list of hrns.
-        persons = defaultdict(list)
-        for person in person_list:
-            persons[person['pointer']].append(person)
-
-        # get the pl records
-        pl_person_list, pl_persons = [], {}
-        pl_person_list = self.plshell.GetPersons(self.plauth, person_ids, ['person_id', 'roles'])
-        pl_persons = list_to_dict(pl_person_list, 'person_id')
-
-        # fill sfa info
-        for record in records:
-            # skip records with no pl info (top level authorities)
-            #if record['pointer'] == -1:
-            #    continue 
-            sfa_info = {}
-            type = record['type']
-            if (type == "slice"):
-                # all slice users are researchers
-                record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
-                record['PI'] = []
-                record['researcher'] = []
-                for person_id in record.get('person_ids', []):
-                    hrns = [person['hrn'] for person in persons[person_id]]
-                    record['researcher'].extend(hrns)                
-
-                # pis at the slice's site
-                if 'site_id' in record and record['site_id'] in site_pis:
-                    pl_pis = site_pis[record['site_id']]
-                    pi_ids = [pi['person_id'] for pi in pl_pis]
-                    for person_id in pi_ids:
-                        hrns = [person['hrn'] for person in persons[person_id]]
-                        record['PI'].extend(hrns)
-                        record['geni_creator'] = record['PI'] 
-                
-            elif (type.startswith("authority")):
-                record['url'] = None
-                if record['hrn'] in self.aggregates:
-                    
-                    record['url'] = self.aggregates[record['hrn']].get_url()
-
-                if record['pointer'] != -1:
-                    record['PI'] = []
-                    record['operator'] = []
-                    record['owner'] = []
-                    for pointer in record.get('person_ids', []):
-                        if pointer not in persons or pointer not in pl_persons:
-                            # this means there is not sfa or pl record for this user
-                            continue   
-                        hrns = [person['hrn'] for person in persons[pointer]] 
-                        roles = pl_persons[pointer]['roles']   
-                        if 'pi' in roles:
-                            record['PI'].extend(hrns)
-                        if 'tech' in roles:
-                            record['operator'].extend(hrns)
-                        if 'admin' in roles:
-                            record['owner'].extend(hrns)
-                        # xxx TODO: OrganizationName
-            elif (type == "node"):
-                sfa_info['dns'] = record.get("hostname", "")
-                # xxx TODO: URI, LatLong, IP, DNS
-    
-            elif (type == "user"):
-                sfa_info['email'] = record.get("email", "")
-                sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
-                sfa_info['geni_certificate'] = record['gid'] 
-                # xxx TODO: PostalAddress, Phone
-            record.update(sfa_info)
-
-    def fill_record_info(self, records):
-        """
-        Given a SFA record, fill in the PLC specific and SFA specific
-        fields in the record. 
-        """
-        if not isinstance(records, list):
-            records = [records]
-
-        self.fill_record_pl_info(records)
-        self.fill_record_sfa_info(records)
-
-    def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
-        # get a list of the HRNs tht are members of the old and new records
-        if oldRecord:
-            oldList = oldRecord.get(listName, [])
-        else:
-            oldList = []     
-        newList = record.get(listName, [])
-
-        # if the lists are the same, then we don't have to update anything
-        if (oldList == newList):
-            return
-
-        # build a list of the new person ids, by looking up each person to get
-        # their pointer
-        newIdList = []
-        table = self.SfaTable()
-        records = table.find({'type': 'user', 'hrn': newList})
-        for rec in records:
-            newIdList.append(rec['pointer'])
-
-        # build a list of the old person ids from the person_ids field 
-        if oldRecord:
-            oldIdList = oldRecord.get("person_ids", [])
-            containerId = oldRecord.get_pointer()
-        else:
-            # if oldRecord==None, then we are doing a Register, instead of an
-            # update.
-            oldIdList = []
-            containerId = record.get_pointer()
-
-    # add people who are in the new list, but not the oldList
-        for personId in newIdList:
-            if not (personId in oldIdList):
-                addFunc(self.plauth, personId, containerId)
-
-        # remove people who are in the old list, but not the new list
-        for personId in oldIdList:
-            if not (personId in newIdList):
-                delFunc(self.plauth, personId, containerId)
-
-    def update_membership(self, oldRecord, record):
-        if record.type == "slice":
-            self.update_membership_list(oldRecord, record, 'researcher',
-                                        self.plshell.AddPersonToSlice,
-                                        self.plshell.DeletePersonFromSlice)
-        elif record.type == "authority":
-            # xxx TODO
-            pass
-
-
-
-class ComponentAPI(BaseAPI):
-
-    def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8", methods='sfa.methods',
-                 peer_cert = None, interface = None, key_file = None, cert_file = None):
-
-        BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, peer_cert=peer_cert,
-                         interface=interface, key_file=key_file, cert_file=cert_file)
-        self.encoding = encoding
-
-        # Better just be documenting the API
-        if config is None:
-            return
-
-        self.nodemanager = NodeManager(self.config)
-
-    def sliver_exists(self):
-        sliver_dict = self.nodemanager.GetXIDs()
-        if slicename in sliver_dict.keys():
-            return True
-        else:
-            return False
-
-    def get_registry(self):
-        addr, port = self.config.SFA_REGISTRY_HOST, self.config.SFA_REGISTRY_PORT
-        url = "http://%(addr)s:%(port)s" % locals()
-        server = xmlrpcprotocol.get_server(url, self.key_file, self.cert_file)
-        return server
-
-    def get_node_key(self):
-        # this call requires no authentication,
-        # so we can generate a random keypair here
-        subject="component"
-        (kfd, keyfile) = tempfile.mkstemp()
-        (cfd, certfile) = tempfile.mkstemp()
-        key = Keypair(create=True)
-        key.save_to_file(keyfile)
-        cert = Certificate(subject=subject)
-        cert.set_issuer(key=key, subject=subject)
-        cert.set_pubkey(key)
-        cert.sign()
-        cert.save_to_file(certfile)
-        registry = self.get_registry()
-        # the registry will scp the key onto the node
-        registry.get_key()        
-
-    def getCredential(self):
-        """
-        Get our credential from a remote registry
-        """
-        path = self.config.SFA_DATA_DIR
-        config_dir = self.config.config_path
-        cred_filename = path + os.sep + 'node.cred'
-        try:
-            credential = Credential(filename = cred_filename)
-            return credential.save_to_string(save_parents=True)
-        except IOError:
-            node_pkey_file = config_dir + os.sep + "node.key"
-            node_gid_file = config_dir + os.sep + "node.gid"
-            cert_filename = path + os.sep + 'server.cert'
-            if not os.path.exists(node_pkey_file) or \
-               not os.path.exists(node_gid_file):
-                self.get_node_key()
-
-            # get node's hrn
-            gid = GID(filename=node_gid_file)
-            hrn = gid.get_hrn()
-            # get credential from registry
-            cert_str = Certificate(filename=cert_filename).save_to_string(save_parents=True)
-            registry = self.get_registry()
-            cred = registry.GetSelfCredential(cert_str, hrn, 'node')
-            Credential(string=cred).save_to_file(credfile, save_parents=True)            
-
-            return cred
-
-    def clean_key_cred(self):
-        """
-        remove the existing keypair and cred  and generate new ones
-        """
-        files = ["server.key", "server.cert", "node.cred"]
-        for f in files:
-            filepath = KEYDIR + os.sep + f
-            if os.path.isfile(filepath):
-                os.unlink(f)
-
-        # install the new key pair
-        # GetCredential will take care of generating the new keypair
-        # and credential
-        self.get_node_key()
-        self.getCredential()
-
-    
index 9276fb0..5b2983b 100644 (file)
@@ -6,7 +6,7 @@ from StringIO import StringIO
 from lxml import etree
 from xmlbuilder import XMLBuilder
 
-from sfa.util.faults import *
+from sfa.util.faults import InvalidRSpec
 from sfa.util.xrn import get_authority
 from sfa.util.plxrn import hrn_to_pl_slicename, hostname_to_urn
 
diff --git a/sfa/plc/plccomponentapi.py b/sfa/plc/plccomponentapi.py
new file mode 100644 (file)
index 0000000..d326482
--- /dev/null
@@ -0,0 +1,112 @@
+import os
+import tempfile
+
+import sfa.util.xmlrpcprotocol as xmlrpcprotocol
+from sfa.util.nodemanager import NodeManager
+
+from sfa.trust.credential import Credential
+from sfa.trust.certificate import Certificate, Keypair
+from sfa.trust.gid import GID
+
+from sfa.server.sfaapi import SfaApi
+
+####################
+class PlcComponentApi(SfaApi):
+    """
+    This class is the type for the toplevel 'api' object 
+    when running the component manager inside a planetlab node.
+    As such it runs an SFA-compliant interface and thus inherits SfaApi
+    However the fact that we run inside a planetlab nodes requires 
+    some tweaks as compared with a service running in the infrastructure.
+    """
+
+    def __init__ (self, encoding="utf-8", methods='sfa.methods', 
+                  config = "/etc/sfa/sfa_config.py", 
+                  peer_cert = None, interface = None, 
+                  key_file = None, cert_file = None, cache = None):
+        SfaApi.__init__(self, encoding=encoding, methods=methods, 
+                        config=config, 
+                        peer_cert=peer_cert, interface=interface, 
+                        key_file=key_file, 
+                        cert_file=cert_file, cache=cache)
+
+        self.nodemanager = NodeManager(self.config)
+
+    def sliver_exists(self):
+        sliver_dict = self.nodemanager.GetXIDs()
+        ### xxx slicename is undefined
+        if slicename in sliver_dict.keys():
+            return True
+        else:
+            return False
+
+    def get_registry(self):
+        addr, port = self.config.SFA_REGISTRY_HOST, self.config.SFA_REGISTRY_PORT
+        url = "http://%(addr)s:%(port)s" % locals()
+        server = xmlrpcprotocol.get_server(url, self.key_file, self.cert_file)
+        return server
+
+    def get_node_key(self):
+        # this call requires no authentication,
+        # so we can generate a random keypair here
+        subject="component"
+        (kfd, keyfile) = tempfile.mkstemp()
+        (cfd, certfile) = tempfile.mkstemp()
+        key = Keypair(create=True)
+        key.save_to_file(keyfile)
+        cert = Certificate(subject=subject)
+        cert.set_issuer(key=key, subject=subject)
+        cert.set_pubkey(key)
+        cert.sign()
+        cert.save_to_file(certfile)
+        registry = self.get_registry()
+        # the registry will scp the key onto the node
+        registry.get_key()        
+
+    # override the method in SfaApi
+    def getCredential(self):
+        """
+        Get our credential from a remote registry
+        """
+        path = self.config.SFA_DATA_DIR
+        config_dir = self.config.config_path
+        cred_filename = path + os.sep + 'node.cred'
+        try:
+            credential = Credential(filename = cred_filename)
+            return credential.save_to_string(save_parents=True)
+        except IOError:
+            node_pkey_file = config_dir + os.sep + "node.key"
+            node_gid_file = config_dir + os.sep + "node.gid"
+            cert_filename = path + os.sep + 'server.cert'
+            if not os.path.exists(node_pkey_file) or \
+               not os.path.exists(node_gid_file):
+                self.get_node_key()
+
+            # get node's hrn
+            gid = GID(filename=node_gid_file)
+            hrn = gid.get_hrn()
+            # get credential from registry
+            cert_str = Certificate(filename=cert_filename).save_to_string(save_parents=True)
+            registry = self.get_registry()
+            cred = registry.GetSelfCredential(cert_str, hrn, 'node')
+            # xxx credfile is undefined
+            Credential(string=cred).save_to_file(credfile, save_parents=True)            
+
+            return cred
+
+    def clean_key_cred(self):
+        """
+        remove the existing keypair and cred  and generate new ones
+        """
+        files = ["server.key", "server.cert", "node.cred"]
+        for f in files:
+            # xxx KEYDIR is undefined, could be meant to be "/var/lib/sfa/" from sfa_component_setup.py
+            filepath = KEYDIR + os.sep + f
+            if os.path.isfile(filepath):
+                os.unlink(f)
+
+        # install the new key pair
+        # GetCredential will take care of generating the new keypair
+        # and credential
+        self.get_node_key()
+        self.getCredential()
diff --git a/sfa/plc/plcsfaapi.py b/sfa/plc/plcsfaapi.py
new file mode 100644 (file)
index 0000000..842df31
--- /dev/null
@@ -0,0 +1,429 @@
+import xmlrpclib
+#
+from sfa.util.faults import MissingSfaInfo
+from sfa.util.sfalogging import logger
+from sfa.util.table import SfaTable
+from sfa.util.defaultdict import defaultdict
+
+from sfa.util.xrn import hrn_to_urn
+from sfa.util.plxrn import slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_login_base
+
+from sfa.server.sfaapi import SfaApi
+
+def list_to_dict(recs, key):
+    """
+    convert a list of dictionaries into a dictionary keyed on the 
+    specified dictionary key 
+    """
+    keys = [rec[key] for rec in recs]
+    return dict(zip(keys, recs))
+
+class PlcSfaApi(SfaApi):
+
+    def __init__ (self, encoding="utf-8", methods='sfa.methods', 
+                  config = "/etc/sfa/sfa_config.py", 
+                  peer_cert = None, interface = None, 
+                  key_file = None, cert_file = None, cache = None):
+        SfaApi.__init__(self, encoding=encoding, methods=methods, 
+                        config=config, 
+                        peer_cert=peer_cert, interface=interface, 
+                        key_file=key_file, 
+                        cert_file=cert_file, cache=cache)
+        self.SfaTable = SfaTable
+        # Initialize the PLC shell only if SFA wraps a myPLC
+        rspec_type = self.config.get_aggregate_type()
+        if (rspec_type == 'pl' or rspec_type == 'vini' or \
+            rspec_type == 'eucalyptus' or rspec_type == 'max'):
+            self.plshell = self.getPLCShell()
+            self.plshell_version = "4.3"
+
+    def getPLCShell(self):
+        self.plauth = {'Username': self.config.SFA_PLC_USER,
+                       'AuthMethod': 'password',
+                       'AuthString': self.config.SFA_PLC_PASSWORD}
+
+        # The native shell (PLC.Shell.Shell) is more efficient than xmlrpc,
+        # but it leaves idle db connections open. use xmlrpc until we can figure
+        # out why PLC.Shell.Shell doesn't close db connection properly     
+        #try:
+        #    sys.path.append(os.path.dirname(os.path.realpath("/usr/bin/plcsh")))
+        #    self.plshell_type = 'direct'
+        #    import PLC.Shell
+        #    shell = PLC.Shell.Shell(globals = globals())
+        #except:
+        
+        self.plshell_type = 'xmlrpc' 
+        url = self.config.SFA_PLC_URL
+        shell = xmlrpclib.Server(url, verbose = 0, allow_none = True)
+        return shell
+
+    ##
+    # Convert SFA fields to PLC fields for use when registering up updating
+    # registry record in the PLC database
+    #
+    # @param type type of record (user, slice, ...)
+    # @param hrn human readable name
+    # @param sfa_fields dictionary of SFA fields
+    # @param pl_fields dictionary of PLC fields (output)
+
+    def sfa_fields_to_pl_fields(self, type, hrn, record):
+
+        def convert_ints(tmpdict, int_fields):
+            for field in int_fields:
+                if field in tmpdict:
+                    tmpdict[field] = int(tmpdict[field])
+
+        pl_record = {}
+        #for field in record:
+        #    pl_record[field] = record[field]
+        if type == "slice":
+            if not "instantiation" in pl_record:
+                pl_record["instantiation"] = "plc-instantiated"
+            pl_record["name"] = hrn_to_pl_slicename(hrn)
+           if "url" in record:
+               pl_record["url"] = record["url"]
+           if "description" in record:
+               pl_record["description"] = record["description"]
+           if "expires" in record:
+               pl_record["expires"] = int(record["expires"])
+
+        elif type == "node":
+            if not "hostname" in pl_record:
+                if not "hostname" in record:
+                    raise MissingSfaInfo("hostname")
+                pl_record["hostname"] = record["hostname"]
+            if not "model" in pl_record:
+                pl_record["model"] = "geni"
+
+        elif type == "authority":
+            pl_record["login_base"] = hrn_to_pl_login_base(hrn)
+
+            if not "name" in pl_record:
+                pl_record["name"] = hrn
+
+            if not "abbreviated_name" in pl_record:
+                pl_record["abbreviated_name"] = hrn
+
+            if not "enabled" in pl_record:
+                pl_record["enabled"] = True
+
+            if not "is_public" in pl_record:
+                pl_record["is_public"] = True
+
+        return pl_record
+
+    def fill_record_pl_info(self, records):
+        """
+        Fill in the planetlab specific fields of a SFA record. This
+        involves calling the appropriate PLC method to retrieve the 
+        database record for the object.
+        
+        PLC data is filled into the pl_info field of the record.
+    
+        @param record: record to fill in field (in/out param)     
+        """
+        # get ids by type
+        node_ids, site_ids, slice_ids = [], [], [] 
+        person_ids, key_ids = [], []
+        type_map = {'node': node_ids, 'authority': site_ids,
+                    'slice': slice_ids, 'user': person_ids}
+                  
+        for record in records:
+            for type in type_map:
+                if type == record['type']:
+                    type_map[type].append(record['pointer'])
+
+        # get pl records
+        nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
+        if node_ids:
+            node_list = self.plshell.GetNodes(self.plauth, node_ids)
+            nodes = list_to_dict(node_list, 'node_id')
+        if site_ids:
+            site_list = self.plshell.GetSites(self.plauth, site_ids)
+            sites = list_to_dict(site_list, 'site_id')
+        if slice_ids:
+            slice_list = self.plshell.GetSlices(self.plauth, slice_ids)
+            slices = list_to_dict(slice_list, 'slice_id')
+        if person_ids:
+            person_list = self.plshell.GetPersons(self.plauth, person_ids)
+            persons = list_to_dict(person_list, 'person_id')
+            for person in persons:
+                key_ids.extend(persons[person]['key_ids'])
+
+        pl_records = {'node': nodes, 'authority': sites,
+                      'slice': slices, 'user': persons}
+
+        if key_ids:
+            key_list = self.plshell.GetKeys(self.plauth, key_ids)
+            keys = list_to_dict(key_list, 'key_id')
+
+        # fill record info
+        for record in records:
+            # records with pointer==-1 do not have plc info.
+            # for example, the top level authority records which are
+            # authorities, but not PL "sites"
+            if record['pointer'] == -1:
+                continue
+           
+            for type in pl_records:
+                if record['type'] == type:
+                    if record['pointer'] in pl_records[type]:
+                        record.update(pl_records[type][record['pointer']])
+                        break
+            # fill in key info
+            if record['type'] == 'user':
+                if 'key_ids' not in record:
+                    logger.info("user record has no 'key_ids' - need to import from myplc ?")
+                else:
+                    pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys] 
+                    record['keys'] = pubkeys
+
+        # fill in record hrns
+        records = self.fill_record_hrns(records)   
+        return records
+
+    def fill_record_hrns(self, records):
+        """
+        convert pl ids to hrns
+        """
+
+        # get ids
+        slice_ids, person_ids, site_ids, node_ids = [], [], [], []
+        for record in records:
+            if 'site_id' in record:
+                site_ids.append(record['site_id'])
+            if 'site_ids' in record:
+                site_ids.extend(record['site_ids'])
+            if 'person_ids' in record:
+                person_ids.extend(record['person_ids'])
+            if 'slice_ids' in record:
+                slice_ids.extend(record['slice_ids'])
+            if 'node_ids' in record:
+                node_ids.extend(record['node_ids'])
+
+        # get pl records
+        slices, persons, sites, nodes = {}, {}, {}, {}
+        if site_ids:
+            site_list = self.plshell.GetSites(self.plauth, site_ids, ['site_id', 'login_base'])
+            sites = list_to_dict(site_list, 'site_id')
+        if person_ids:
+            person_list = self.plshell.GetPersons(self.plauth, person_ids, ['person_id', 'email'])
+            persons = list_to_dict(person_list, 'person_id')
+        if slice_ids:
+            slice_list = self.plshell.GetSlices(self.plauth, slice_ids, ['slice_id', 'name'])
+            slices = list_to_dict(slice_list, 'slice_id')       
+        if node_ids:
+            node_list = self.plshell.GetNodes(self.plauth, node_ids, ['node_id', 'hostname'])
+            nodes = list_to_dict(node_list, 'node_id')
+       
+        # convert ids to hrns
+        for record in records:
+            # get all relevant data
+            type = record['type']
+            pointer = record['pointer']
+            auth_hrn = self.hrn
+            login_base = ''
+            if pointer == -1:
+                continue
+
+            if 'site_id' in record:
+                site = sites[record['site_id']]
+                login_base = site['login_base']
+                record['site'] = ".".join([auth_hrn, login_base])
+            if 'person_ids' in record:
+                emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
+                          if person_id in  persons]
+                usernames = [email.split('@')[0] for email in emails]
+                person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
+                record['persons'] = person_hrns 
+            if 'slice_ids' in record:
+                slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
+                              if slice_id in slices]
+                slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
+                record['slices'] = slice_hrns
+            if 'node_ids' in record:
+                hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
+                             if node_id in nodes]
+                node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
+                record['nodes'] = node_hrns
+            if 'site_ids' in record:
+                login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
+                               if site_id in sites]
+                site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
+                record['sites'] = site_hrns
+            
+        return records   
+
+    def fill_record_sfa_info(self, records):
+
+        def startswith(prefix, values):
+            return [value for value in values if value.startswith(prefix)]
+
+        # get person ids
+        person_ids = []
+        site_ids = []
+        for record in records:
+            person_ids.extend(record.get("person_ids", []))
+            site_ids.extend(record.get("site_ids", [])) 
+            if 'site_id' in record:
+                site_ids.append(record['site_id']) 
+        
+        # get all pis from the sites we've encountered
+        # and store them in a dictionary keyed on site_id 
+        site_pis = {}
+        if site_ids:
+            pi_filter = {'|roles': ['pi'], '|site_ids': site_ids} 
+            pi_list = self.plshell.GetPersons(self.plauth, pi_filter, ['person_id', 'site_ids'])
+            for pi in pi_list:
+                # we will need the pi's hrns also
+                person_ids.append(pi['person_id'])
+                
+                # we also need to keep track of the sites these pis
+                # belong to
+                for site_id in pi['site_ids']:
+                    if site_id in site_pis:
+                        site_pis[site_id].append(pi)
+                    else:
+                        site_pis[site_id] = [pi]
+                 
+        # get sfa records for all records associated with these records.   
+        # we'll replace pl ids (person_ids) with hrns from the sfa records
+        # we obtain
+        
+        # get the sfa records
+        table = self.SfaTable()
+        person_list, persons = [], {}
+        person_list = table.find({'type': 'user', 'pointer': person_ids})
+        # create a hrns keyed on the sfa record's pointer.
+        # Its possible for  multiple records to have the same pointer so
+        # the dict's value will be a list of hrns.
+        persons = defaultdict(list)
+        for person in person_list:
+            persons[person['pointer']].append(person)
+
+        # get the pl records
+        pl_person_list, pl_persons = [], {}
+        pl_person_list = self.plshell.GetPersons(self.plauth, person_ids, ['person_id', 'roles'])
+        pl_persons = list_to_dict(pl_person_list, 'person_id')
+
+        # fill sfa info
+        for record in records:
+            # skip records with no pl info (top level authorities)
+            #if record['pointer'] == -1:
+            #    continue 
+            sfa_info = {}
+            type = record['type']
+            if (type == "slice"):
+                # all slice users are researchers
+                record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
+                record['PI'] = []
+                record['researcher'] = []
+                for person_id in record.get('person_ids', []):
+                    hrns = [person['hrn'] for person in persons[person_id]]
+                    record['researcher'].extend(hrns)                
+
+                # pis at the slice's site
+                if 'site_id' in record and record['site_id'] in site_pis:
+                    pl_pis = site_pis[record['site_id']]
+                    pi_ids = [pi['person_id'] for pi in pl_pis]
+                    for person_id in pi_ids:
+                        hrns = [person['hrn'] for person in persons[person_id]]
+                        record['PI'].extend(hrns)
+                        record['geni_creator'] = record['PI'] 
+                
+            elif (type.startswith("authority")):
+                record['url'] = None
+                if record['hrn'] in self.aggregates:
+                    
+                    record['url'] = self.aggregates[record['hrn']].get_url()
+
+                if record['pointer'] != -1:
+                    record['PI'] = []
+                    record['operator'] = []
+                    record['owner'] = []
+                    for pointer in record.get('person_ids', []):
+                        if pointer not in persons or pointer not in pl_persons:
+                            # this means there is not sfa or pl record for this user
+                            continue   
+                        hrns = [person['hrn'] for person in persons[pointer]] 
+                        roles = pl_persons[pointer]['roles']   
+                        if 'pi' in roles:
+                            record['PI'].extend(hrns)
+                        if 'tech' in roles:
+                            record['operator'].extend(hrns)
+                        if 'admin' in roles:
+                            record['owner'].extend(hrns)
+                        # xxx TODO: OrganizationName
+            elif (type == "node"):
+                sfa_info['dns'] = record.get("hostname", "")
+                # xxx TODO: URI, LatLong, IP, DNS
+    
+            elif (type == "user"):
+                sfa_info['email'] = record.get("email", "")
+                sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
+                sfa_info['geni_certificate'] = record['gid'] 
+                # xxx TODO: PostalAddress, Phone
+            record.update(sfa_info)
+
+    def fill_record_info(self, records):
+        """
+        Given a SFA record, fill in the PLC specific and SFA specific
+        fields in the record. 
+        """
+        if not isinstance(records, list):
+            records = [records]
+
+        self.fill_record_pl_info(records)
+        self.fill_record_sfa_info(records)
+
+    def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
+        # get a list of the HRNs that are members of the old and new records
+        if oldRecord:
+            oldList = oldRecord.get(listName, [])
+        else:
+            oldList = []     
+        newList = record.get(listName, [])
+
+        # if the lists are the same, then we don't have to update anything
+        if (oldList == newList):
+            return
+
+        # build a list of the new person ids, by looking up each person to get
+        # their pointer
+        newIdList = []
+        table = self.SfaTable()
+        records = table.find({'type': 'user', 'hrn': newList})
+        for rec in records:
+            newIdList.append(rec['pointer'])
+
+        # build a list of the old person ids from the person_ids field 
+        if oldRecord:
+            oldIdList = oldRecord.get("person_ids", [])
+            containerId = oldRecord.get_pointer()
+        else:
+            # if oldRecord==None, then we are doing a Register, instead of an
+            # update.
+            oldIdList = []
+            containerId = record.get_pointer()
+
+    # add people who are in the new list, but not the oldList
+        for personId in newIdList:
+            if not (personId in oldIdList):
+                addFunc(self.plauth, personId, containerId)
+
+        # remove people who are in the old list, but not the new list
+        for personId in oldIdList:
+            if not (personId in newIdList):
+                delFunc(self.plauth, personId, containerId)
+
+    def update_membership(self, oldRecord, record):
+        if record.type == "slice":
+            self.update_membership_list(oldRecord, record, 'researcher',
+                                        self.plshell.AddPersonToSlice,
+                                        self.plshell.DeletePersonFromSlice)
+        elif record.type == "authority":
+            # xxx TODO
+            pass
index 95793a1..d57b4b8 100755 (executable)
 # RSA keys at this time, not DSA keys.
 ##
 
+import os
 import getopt
 import sys
-import tempfile
 
-from sfa.util.record import *
 from sfa.util.table import SfaTable
 from sfa.util.xrn import get_leaf, get_authority
 from sfa.util.plxrn import hostname_to_hrn, slicename_to_hrn, email_to_hrn, hrn_to_pl_slicename
 from sfa.util.config import Config
-from sfa.trust.certificate import convert_public_key, Keypair
-from sfa.trust.trustedroots import *
-from sfa.trust.hierarchy import *
 from sfa.util.xrn import Xrn
-from sfa.plc.api import *
-from sfa.trust.gid import create_uuid
-from sfa.plc.sfaImport import sfaImport, _cleanup_string
+
+from sfa.plc.sfaImport import sfaImport
 
 def process_options():
 
@@ -125,7 +120,8 @@ def main():
         sites_dict[site['login_base']] = site 
     
     # Get all plc users
-    persons = shell.GetPersons(plc_auth, {'peer_id': None, 'enabled': True}, ['person_id', 'email', 'key_ids', 'site_ids'])
+    persons = shell.GetPersons(plc_auth, {'peer_id': None, 'enabled': True}, 
+                               ['person_id', 'email', 'key_ids', 'site_ids'])
     persons_dict = {}
     for person in persons:
         persons_dict[person['person_id']] = person
index fb84020..be7b0c1 100755 (executable)
@@ -11,8 +11,6 @@ import sys
 import os
 from optparse import OptionParser
 
-from sfa.trust.hierarchy import *
-from sfa.util.record import *
 from sfa.util.table import SfaTable
 from sfa.util.sfalogging import logger
 
index 1effe71..4de6e1b 100644 (file)
@@ -8,20 +8,16 @@
 # RSA keys at this time, not DSA keys.
 ##
 
-import getopt
-import sys
-import tempfile
-
 from sfa.util.sfalogging import _SfaLogger
 
-from sfa.util.record import *
+from sfa.util.record import SfaRecord
 from sfa.util.table import SfaTable
 from sfa.util.xrn import get_authority, hrn_to_urn
 from sfa.util.plxrn import email_to_hrn
 from sfa.util.config import Config
 from sfa.trust.certificate import convert_public_key, Keypair
 from sfa.trust.trustedroots import TrustedRoots
-from sfa.trust.hierarchy import *
+from sfa.trust.hierarchy import Hierarchy
 from sfa.trust.gid import create_uuid
 
 
@@ -234,8 +230,6 @@ class sfaImport:
 
     
     def import_site(self, hrn, site):
-        shell = self.shell
-        plc_auth = self.plc_auth
         urn = hrn_to_urn(hrn, 'authority')
         self.logger.info("Import: site %s"%hrn)
 
index 78ba4b9..5cead3b 100644 (file)
@@ -1,19 +1,13 @@
-import datetime
-import time
-import traceback
-import sys
-
 from types import StringTypes
-from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn, urn_to_hrn
-from sfa.util.plxrn import hrn_to_pl_slicename, hrn_to_pl_login_base
-from sfa.util.rspec import *
-from sfa.util.specdict import *
-from sfa.util.faults import *
-from sfa.util.record import SfaRecord
-from sfa.util.policy import Policy
-from sfa.util.prefixTree import prefixTree
 from collections import defaultdict
 
+from sfa.util.xrn import get_leaf, get_authority, urn_to_hrn
+from sfa.util.plxrn import hrn_to_pl_slicename
+from sfa.util.policy import Policy
+from sfa.rspecs.rspec import RSpec
+from sfa.plc.vlink import VLink
+from sfa.util.xrn import Xrn
+
 MAXINT =  2L**31-1
 
 class Slices:
@@ -27,6 +21,8 @@ class Slices:
         self.origin_hrn = origin_hrn
         self.registry = api.registries[api.hrn]
         self.credential = api.getCredential()
+        self.nodes = []
+        self.persons = []
 
     def get_slivers(self, xrn, node=None):
         hrn, type = urn_to_hrn(xrn)
@@ -188,6 +184,24 @@ class Slices:
         except: 
             self.api.logger.log_exc('Failed to add/remove slice from nodes')
 
+    def verify_slice_links(self, slice, links, aggregate):
+        # nodes is undefined here
+        if not links:
+            return 
+        
+        for link in links:
+            # get the ip address of the first node in the link
+            ifname1 = Xrn(link['interface1']['component_id']).get_leaf()
+            (node, device) = ifname1.split(':')
+            node_id = int(node.replace('node', ''))
+            node = aggregate.nodes[node_id]
+            if1 = aggregate.interfaces[node['interface_ids'][0]]
+            ipaddr = if1['ip']
+            topo_rspec = VLink.get_topo_rspec(link, ipaddr)
+            self.api.plshell.AddSliceTag(self.api.plauth, slice['name'], 'topo_rspec', str([topo_rspec]), node_id) 
+                        
+        
+
     def handle_peer(self, site, slice, persons, peer):
         if peer:
             # bind site
@@ -533,8 +547,7 @@ class Slices:
         # add requested_attributes
         for attribute in added_slice_attributes:
             try:
-                name, value, node_id = attribute['name'], attribute['value'], attribute.get('node_id', None)
-                self.api.plshell.AddSliceTag(self.api.plauth, slice['name'], name, value, node_id)
+                self.api.plshell.AddSliceTag(self.api.plauth, slice['name'], attribute['name'], attribute['value'], attribute.get('node_id', None))
             except Exception, e:
                 self.api.logger.warn('Failed to add sliver attribute. name: %s, value: %s, node_id: %s\nCause:%s'\
                                 % (name, value,  node_id, str(e)))
diff --git a/sfa/plc/vlink.py b/sfa/plc/vlink.py
new file mode 100644 (file)
index 0000000..625963d
--- /dev/null
@@ -0,0 +1,115 @@
+import re
+from sfa.util.xrn import Xrn
+# Taken from bwlimit.py
+#
+# See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
+# warned that older versions of tc interpret "kbps", "mbps", "mbit",
+# and "kbit" to mean (in this system) "kibps", "mibps", "mibit", and
+# "kibit" and that if an older version is installed, all rates will
+# be off by a small fraction.
+suffixes = {
+    "":         1,
+    "bit":  1,
+    "kibit":    1024,
+    "kbit": 1000,
+    "mibit":    1024*1024,
+    "mbit": 1000000,
+    "gibit":    1024*1024*1024,
+    "gbit": 1000000000,
+    "tibit":    1024*1024*1024*1024,
+    "tbit": 1000000000000,
+    "bps":  8,
+    "kibps":    8*1024,
+    "kbps": 8000,
+    "mibps":    8*1024*1024,
+    "mbps": 8000000,
+    "gibps":    8*1024*1024*1024,
+    "gbps": 8000000000,
+    "tibps":    8*1024*1024*1024*1024,
+    "tbps": 8000000000000
+}
+
+
+def get_tc_rate(s):
+    """
+    Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
+    """
+
+    if type(s) == int:
+        return s
+    m = re.match(r"([0-9.]+)(\D*)", s)
+    if m is None:
+        return -1
+    suffix = m.group(2).lower()
+    if suffixes.has_key(suffix):
+        return int(float(m.group(1)) * suffixes[suffix])
+    else:
+        return -1
+
+def format_tc_rate(rate):
+    """
+    Formats a bits/second rate into a tc rate string
+    """
+
+    if rate >= 1000000000 and (rate % 1000000000) == 0:
+        return "%.0fgbit" % (rate / 1000000000.)
+    elif rate >= 1000000 and (rate % 1000000) == 0:
+        return "%.0fmbit" % (rate / 1000000.)
+    elif rate >= 1000:
+        return "%.0fkbit" % (rate / 1000.)
+    else:
+        return "%.0fbit" % rate
+
+class VLink:
+    @staticmethod
+    def get_link_id(if1, if2):
+        if if1['id'] < if2['id']:
+            link = (if1['id']<<7) + if2['id']
+        else:
+            link = (if2['id']<<7) + if1['id']
+        return link
+
+    @staticmethod
+    def get_iface_id(if1, if2):
+        if if1['id'] < if2['id']:
+            iface = 1
+        else:
+            iface = 2
+        return iface
+
+    @staticmethod
+    def get_virt_ip(if1, if2):
+        link_id = VLink.get_link_id(if1, if2)
+        iface_id = VLink.get_iface_id(if1, if2)
+        first = link_id >> 6
+        second = ((link_id & 0x3f)<<2) + iface_id
+        return "192.168.%d.%s" % (first, second)
+
+    @staticmethod
+    def get_virt_net(link):
+        link_id = VLink.get_link_id(link['interface1'], link['interface2'])
+        first = link_id >> 6
+        second = (link_id & 0x3f)<<2
+        return "192.168.%d.%d/30" % (first, second)
+
+    @staticmethod
+    def get_interface_id(interface):
+        if_name = Xrn(interface['component_id']).get_leaf()
+        node, dev = if_name.split(":")
+        node_id = int(node.replace("node", ""))
+        return node_id
+
+        
+    @staticmethod
+    def get_topo_rspec(link, ipaddr):
+        link['interface1']['id'] = VLink.get_interface_id(link['interface1'])
+        link['interface2']['id'] = VLink.get_interface_id(link['interface2'])
+        my_ip = VLink.get_virt_ip(link['interface1'], link['interface2'])
+        remote_ip = VLink.get_virt_ip(link['interface2'], link['interface1'])
+        net = VLink.get_virt_net(link)
+        bw = format_tc_rate(long(link['capacity']))
+        return (link['interface2']['id'], ipaddr, bw, my_ip, remote_ip, net)
+
+    @staticmethod 
+    def topo_rspec_to_link(topo_rspec):
+        pass          
diff --git a/sfa/rspecs/elements/interface.py b/sfa/rspecs/elements/interface.py
new file mode 100644 (file)
index 0000000..2aadf4d
--- /dev/null
@@ -0,0 +1,13 @@
+class Interface(dict):
+    element = None
+    fields = {'component_id': None,
+              'role': None,
+              'client_id': None,
+              'ipv4': None,
+    }    
+    def __init__(self, fields={}, element=None):
+        self.element = element
+        dict.__init__(self, Interface.fields)
+        self.update(fields)
+        
+    
index 682232e..d916d22 100644 (file)
@@ -1,14 +1,23 @@
-from sfa.rspec.elements.interface import Interface
+from sfa.rspecs.elements.interface import Interface
+
+class Link(dict):
+    element = None 
+    fields = {
+        'client_id': None, 
+        'component_id': None,
+        'component_name': None,
+        'component_manager': None,
+        'type': None,
+        'interface1': None,
+        'interface2': None,
+        'capacity': None,
+        'latency': None,
+        'packet_loss': None,
+        'description': None,
+    }
+    
+    def __init__(self, fields={}, element=None):
+        self.element = element
+        dict.__init__(self, Link.fields)
+        self.update(fields)
 
-class Link:
-    def __init__(self):
-        self.component_id = None
-        self.component_name = None
-        self.component_manager_id = None
-        self.type = None
-        self.endpoint1 = Interface()
-        self.endpoint2 = Interface()
-        self.capacity = None
-        self.latency = None
-        self.packet_loss = None
-        self.description = None
diff --git a/sfa/rspecs/elements/versions/__init__.py b/sfa/rspecs/elements/versions/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/sfa/rspecs/elements/versions/pgv2Link.py b/sfa/rspecs/elements/versions/pgv2Link.py
new file mode 100644 (file)
index 0000000..aeef760
--- /dev/null
@@ -0,0 +1,118 @@
+from lxml import etree
+from sfa.util.plxrn import PlXrn
+from sfa.util.xrn import Xrn
+from sfa.rspecs.elements.link import Link
+from sfa.rspecs.elements.interface import Interface
+from sfa.rspecs.rspec_elements import RSpecElement, RSpecElements
+
+class PGv2Link:
+
+    elements = {
+        'link': RSpecElement(RSpecElements.LINK, '//default:link | //link'),
+        'component_manager': RSpecElement(RSpecElements.COMPONENT_MANAGER, './default:component_manager | ./component_manager'),
+        'link_type': RSpecElement(RSpecElements.LINK_TYPE, './default:link_type | ./link_type'),
+        'property': RSpecElement(RSpecElements.PROPERTY, './default:property | ./property'),
+        'interface_ref': RSpecElement(RSpecElements.INTERFACE_REF, './default:interface_ref | ./interface_ref') 
+    }
+    
+    @staticmethod
+    def add_links(xml, links):
+        for link in links:
+            link_elem = etree.SubElement(xml, 'link')
+            for attrib in ['component_name', 'component_id', 'client_id']:
+                if attrib in link and link[attrib] is not None:
+                    link_elem.set(attrib, link[attrib])
+            if 'component_manager' in link and link['component_manager']:
+                cm_element = etree.SubElement(link_elem, 'component_manager', name=link['component_manager'])
+            for if_ref in [link['interface1'], link['interface2']]:
+                if_ref_elem = etree.SubElement(link_elem, 'interface_ref')
+                for attrib in Interface.fields:
+                    if attrib in if_ref and if_ref[attrib]:
+                        if_ref_elem.attrib[attrib] = if_ref[attrib]  
+            prop1 = etree.SubElement(link_elem, 'property', source_id = link['interface1']['component_id'],
+                dest_id = link['interface2']['component_id'], capacity=link['capacity'], 
+                latency=link['latency'], packet_loss=link['packet_loss'])
+            prop2 = etree.SubElement(link_elem, 'property', source_id = link['interface2']['component_id'],
+                dest_id = link['interface1']['component_id'], capacity=link['capacity'], 
+                latency=link['latency'], packet_loss=link['packet_loss'])
+            if 'type' in link and link['type']:
+                type_elem = etree.SubElement(link_elem, 'link_type', name=link['type'])             
+    @staticmethod 
+    def get_links(xml):
+        links = []
+        link_elems = xml.xpath(PGv2Link.elements['link'].path, namespaces=xml.namespaces)
+        for link_elem in link_elems:
+            # set client_id, component_id, component_name
+            link = Link(link_elem.attrib, link_elem)
+            # set component manager
+            cm = link_elem.xpath('./default:component_manager', namespaces=xml.namespaces)
+            if len(cm) >  0:
+                cm = cm[0]
+                if  'name' in cm.attrib:
+                    link['component_manager'] = cm.attrib['name'] 
+            # set link type
+            link_types = link_elem.xpath(PGv2Link.elements['link_type'].path, namespaces=xml.namespaces)
+            if len(link_types) > 0:
+                link_type = link_types[0]
+                if 'name' in link_type.attrib:
+                    link['type'] = link_type.attrib['name']
+          
+            # get capacity, latency and packet_loss from first property  
+            props = link_elem.xpath(PGv2Link.elements['property'].path, namespaces=xml.namespaces)
+            if len(props) > 0:
+                prop = props[0]
+                for attrib in ['capacity', 'latency', 'packet_loss']:
+                    if attrib in prop.attrib:
+                        link[attrib] = prop.attrib[attrib]
+                             
+            # get interfaces 
+            if_elems = link_elem.xpath(PGv2Link.elements['interface_ref'].path, namespaces=xml.namespaces)
+            ifs = []
+            for if_elem in if_elems:
+                if_ref = Interface(if_elem.attrib, if_elem)
+                ifs.append(if_ref)
+            if len(ifs) > 1:
+                link['interface1'] = ifs[0]
+                link['interface2'] = ifs[1] 
+            links.append(link)
+        return links 
+
+    @staticmethod
+    def add_link_requests(xml, link_tuples, append=False):
+        if not isinstance(link_tuples, set):
+            link_tuples = set(link_tuples)
+
+        available_links = PGv2Link.get_links(xml)
+        recently_added = []
+        for link in available_links:
+            if_name1 =  Xrn(link['interface1']['component_id']).get_leaf()
+            if_name2 =  Xrn(link['interface2']['component_id']).get_leaf()
+             
+            requested_link = None
+            l_tup_1 = (if_name1, if_name2)
+            l_tup_2 = (if_name2, if_name1)
+            if link_tuples.issuperset([(if_name1, if_name2)]):
+                requested_link = (if_name1, if_name2)        
+            elif link_tuples.issuperset([(if_name2, if_name2)]):
+                requested_link = (if_name2, if_name1)
+            if requested_link:
+                # add client id to link ane interface elements 
+                link.element.set('client_id', link['component_name'])
+                link['interface1'].element.set('client_id', Xrn(link['interface1']['component_id']).get_leaf()) 
+                link['interface2'].element.set('client_id', Xrn(link['interface2']['component_id']).get_leaf()) 
+                recently_added.append(link['component_name'])
+
+        if not append:
+            # remove all links that don't have a client id 
+            for link in PGv2Link.get_links(xml):
+                if not link['client_id'] or link['component_name'] not in recently_added:
+                    parent = link.element.getparent()
+                    parent.remove(link.element)                  
+             
+    @staticmethod
+    def get_link_requests(xml):
+        link_requests = []
+        for link in PGv2Link.get_links(xml):
+            if link['client_id'] != None:
+                link_requests.append(link)
+        return link_requests           
index 42e7ccd..1c57d7d 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python 
 from lxml import etree
 from StringIO import StringIO
-from sfa.util.xrn import *
+from sfa.util.xrn import Xrn, urn_to_hrn
 from sfa.rspecs.rspec import RSpec
 from sfa.rspecs.version_manager import VersionManager
 
index 73185fe..a04ff28 100755 (executable)
@@ -1,11 +1,11 @@
 #!/usr/bin/python 
 from datetime import datetime, timedelta
-from sfa.rspecs.xml import XML, XpathFilter
-from sfa.rspecs.version_manager import VersionManager
-from sfa.util.xrn import *
-from sfa.util.plxrn import hostname_to_urn
+
+from sfa.util.xml import XML, XpathFilter
+from sfa.util.faults import InvalidRSpecElement
+
 from sfa.rspecs.rspec_elements import RSpecElement, RSpecElements 
-from sfa.util.faults import SfaNotImplemented, InvalidRSpec, InvalidRSpecElement
+from sfa.rspecs.version_manager import VersionManager
 
 class RSpec:
  
@@ -70,7 +70,7 @@ class RSpec:
 
     def get(self, element_type, filter={}, depth=0):
         elements = self.get_elements(element_type, filter)
-        elements = [self.get_element_attributes(element, depth=depth) for element in elements]
+        elements = [self.xml.get_element_attributes(elem, depth=depth) for elem in elements]
         return elements
 
     def get_elements(self, element_type, filter={}):
@@ -83,7 +83,7 @@ class RSpec:
             raise InvalidRSpecElement(element_type, extra=msg)
         rspec_element = self.get_rspec_element(element_type)
         xpath = rspec_element.path + XpathFilter.xpath(filter)
-        return self.xpath(xpath)
+        return self.xml.xpath(xpath)
 
     def merge(self, in_rspec):
         self.version.merge(in_rspec)
index 2eb0d8c..3226f58 100644 (file)
@@ -1,12 +1,22 @@
 from sfa.util.enumeration import Enum
 
 # recognized top level rspec elements
-RSpecElements = Enum('NETWORK', 'NODE', 'SLIVER', 'INTERFACE', 'LINK', 'VLINK')
+RSpecElements = Enum(NETWORK='NETWORK', 
+                     COMPONENT_MANAGER='COMPONENT_MANAGER', 
+                     SLIVER='SLIVER', 
+                     SLIVER_TYPE='SLIVER_TYPE', 
+                     NODE='NODE', 
+                     INTERFACE='INTERFACE', 
+                     INTERFACE_REF='INTERFACE_REF', 
+                     LINK='LINK', 
+                     LINK_TYPE='LINK_TYPE', 
+                     SERVICE='SERVICE',
+                     PROPERTY='PROPERTY'
+                )
 
 class RSpecElement:
-    def __init__(self, element_type, name, path):
+    def __init__(self, element_type, path):
         if not element_type in RSpecElements:
             raise InvalidRSpecElement(element_type)
         self.type = element_type
-        self.name = name
         self.path = path
index 6ba56c1..7bcc787 100755 (executable)
@@ -1,8 +1,6 @@
 #!/usr/bin/python
 
-from lxml import etree
-from StringIO import StringIO
-from sfa.util.xrn import *
+from sfa.util.xrn import hrn_to_urn
 from sfa.rspecs.rspec import RSpec
 from sfa.rspecs.version_manager import VersionManager
 
index 177b32c..b57cd9b 100644 (file)
@@ -1,11 +1,12 @@
 from lxml import etree
 from copy import deepcopy
 from StringIO import StringIO
-from sfa.util.xrn import *
+from sfa.util.xrn import urn_to_sliver_id
 from sfa.util.plxrn import hostname_to_urn, xrn_to_hostname 
 from sfa.rspecs.rspec_version import BaseVersion
 from sfa.rspecs.rspec_elements import RSpecElement, RSpecElements
-
+from sfa.rspecs.elements.versions.pgv2Link import PGv2Link
 class PGv2(BaseVersion):
     type = 'ProtoGENI'
     content_type = 'ad'
@@ -17,11 +18,7 @@ class PGv2(BaseVersion):
         'planetlab': "http://www.planet-lab.org/resources/sfa/ext/planetlab/1",
     }
     namespaces = dict(extensions.items() + [('default', namespace)])
-    elements = [
-        RSpecElement(RSpecElements.NETWORK, 'network', '//default:node[@component_manager_id][1]'),
-        RSpecElement(RSpecElements.NODE, 'node', '//default:node | //node'),
-        RSpecElement(RSpecElements.SLIVER, 'sliver', '//default:node/default:sliver_type | //node/sliver_type'),
-    ]
+    elements = []
 
     def get_network(self):
         network = None
@@ -95,6 +92,18 @@ class PGv2(BaseVersion):
 
         return slice_attributes
 
+    def get_links(self, network=None):
+        return PGv2Link.get_links(self.xml)
+
+    def get_link_requests(self):
+        return PGv2Link.get_link_requests(self.xml)  
+
+    def add_links(self, links):
+        PGv2Link.add_links(self.xml.root, links)
+
+    def add_link_requests(self, link_tuples, append=False):
+        PGv2Link.add_link_requests(self.xml.root, link_tuples, append)
+
     def attributes_list(self, elem):
         opts = []
         if elem is not None:
@@ -129,7 +138,7 @@ class PGv2(BaseVersion):
             node_type_tag = etree.SubElement(node_tag, 'hardware_type', name='plab-pc')
             node_type_tag = etree.SubElement(node_tag, 'hardware_type', name='pc')
             available_tag = etree.SubElement(node_tag, 'available', now='true')
-            sliver_type_tag = etree.SubElement(node_tag, 'sliver_type', name='plab-vnode')
+            sliver_type_tag = etree.SubElement(node_tag, 'sliver_type', name='plab-vserver')
 
             pl_initscripts = node.get('pl_initscripts', {})
             for pl_initscript in pl_initscripts.values():
@@ -149,7 +158,7 @@ class PGv2(BaseVersion):
         # this is untested
         self.xml.root.append(deepcopy(source_node_tag))
 
-    def add_slivers(self, slivers, sliver_urn=None, no_dupes=False):
+    def add_slivers(self, slivers, sliver_urn=None, no_dupes=False, append=False):
 
         # all nodes hould already be present in the rspec. Remove all
         # nodes that done have slivers
@@ -164,19 +173,18 @@ class PGv2(BaseVersion):
         for node in nodes:
             urn = node.get('component_id')
             hostname = xrn_to_hostname(urn)
-            if hostname not in slivers_dict:
+            if hostname not in slivers_dict and not append:
                 parent = node.getparent()
                 parent.remove(node)
             else:
                 sliver_info = slivers_dict[hostname]
-                sliver_type_elements = node.xpath('./sliver_type', namespaces=self.namespaces)
+                sliver_type_elements = node.xpath('./default:sliver_type', namespaces=self.namespaces)
                 available_sliver_types = [element.attrib['name'] for element in sliver_type_elements]
-                valid_sliver_types = ['emulab-openvz', 'raw-pc']
+                valid_sliver_types = ['emulab-openvz', 'raw-pc', 'plab-vserver', 'plab-vnode']
                 requested_sliver_type = None
                 for valid_sliver_type in valid_sliver_types:
-                    if valid_sliver_type in available_sliver_type:
+                    if valid_sliver_type in available_sliver_types:
                         requested_sliver_type = valid_sliver_type
-
                 if requested_sliver_type:
                     # remove existing sliver_type tags,it needs to be recreated
                     sliver_elem = node.xpath('./default:sliver_type | ./sliver_type', namespaces=self.namespaces)
@@ -204,6 +212,7 @@ class PGv2(BaseVersion):
                     parent = node.getparent()
                     parent.remove(node)
 
+    
 
     def remove_slivers(self, slivers, network=None, no_dupes=False):
         for sliver in slivers:
@@ -218,28 +227,6 @@ class PGv2(BaseVersion):
     def add_interfaces(self, interfaces, no_dupes=False):
         pass
 
-    def add_links(self, links, no_dupes=False):
-        for link in links:
-            link_elem = etree.SubElement(self.xml.root, 'link' )
-            link_elem.set('component_name', link.component_name) 
-            link_elem.set('component_id', link.component_id)
-            cm_elem = etree.SubElement(link_elem, 'component_manager')
-            cm_elem.set('name', link.component_manager_name)
-            for endpoint in [link.endpoint1, link.enpoint2]:
-                interface_ref = etree.SubElement(link_elem, 'interface_ref', component_id=endpoint.id)
-                
-            property_attrs = {'capicity': link.capacity, 
-                              'latency': link.latency, 
-                              'packet_loss': link.packet_loss}    
-            property1 = etree.SubElement(link_elem, 'property', source_id=link.endpoint1.id, \
-              dest_id = link.endpoint2.id, capacity = link.capacity, latency=link.latency, \
-              packet_loss = link.packet_loss)
-            
-            property2 = etree.SubElement(link_elem, 'property', source_id=link.endpoint2.id, \
-              dest_id = link.endpoint1.id, capacity = link.capacity, latency=link.latency, \
-              packet_loss = link.packet_loss)
-            link_type = etree.SubElement(link_elem, 'link_type', name=link.type)
-
     def merge(self, in_rspec):
         """
         Merge contents for specified rspec with current rspec
index 1b147c9..3917b39 100644 (file)
@@ -1,7 +1,10 @@
+from copy import deepcopy
 from lxml import etree
 from sfa.util.xrn import hrn_to_urn, urn_to_hrn
+from sfa.util.plxrn import PlXrn
 from sfa.rspecs.rspec_version import BaseVersion
 from sfa.rspecs.rspec_elements import RSpecElement, RSpecElements
+from sfa.rspecs.elements.versions.pgv2Link import PGv2Link
 
 class SFAv1(BaseVersion):
     enabled = True
@@ -12,11 +15,7 @@ class SFAv1(BaseVersion):
     namespace = None
     extensions = {}
     namespaces = None
-    elements = [
-        RSpecElement(RSpecElements.NETWORK, 'network', '//network'),
-        RSpecElement(RSpecElements.NODE, 'node', '//node'),
-        RSpecElement(RSpecElements.SLIVER, 'sliver', '//node/sliver'),
-    ] 
+    elements = [] 
     template = '<RSpec type="%s"></RSpec>' % type
 
     def get_network_elements(self):
@@ -116,18 +115,10 @@ class SFAv1(BaseVersion):
         return nodes
 
     def get_links(self, network=None):
-        if network:
-            links = self.xml.xpath('//network[@name="%s"]/link' % network)
-        else:
-            links = self.xml.xpath('//link')
-        linklist = []
-        for link in links:
-            (end1, end2) = link.get("endpoints").split()
-            name = link.find("description")
-            linklist.append((name.text,
-                             self.get_site_nodes(end1, network),
-                             self.get_site_nodes(end2, network)))
-        return linklist
+        return PGv2Link.get_links(self.xml)
+
+    def get_link_requests(self):
+        return PGv2Link.get_link_requests(self.xml) 
 
     def get_link(self, fromnode, tonode, network=None):
         fromsite = fromnode.getparent()
@@ -222,9 +213,14 @@ class SFAv1(BaseVersion):
                 node_tag.set('component_name', node['hostname']) 
                 hostname_tag = etree.SubElement(node_tag, 'hostname').text = node['hostname']
             if 'interfaces' in node:
+                i = 0
                 for interface in node['interfaces']:
                     if 'bwlimit' in interface and interface['bwlimit']:
                         bwlimit = etree.SubElement(node_tag, 'bw_limit', units='kbps').text = str(interface['bwlimit']/1000)
+                    comp_id = PlXrn(auth=network, interface='node%s:eth%s' % (node['node_id'], i)).get_urn() 
+                    ipaddr = interface['ip'] 
+                    interface_tag = etree.SubElement(node_tag, 'interface', component_id=comp_id, ipv4=ipaddr)
+                    i+=1
             if 'bw_unallocated' in node:
                 bw_unallocated = etree.SubElement(node_tag, 'bw_unallocated', units='kbps').text = str(node['bw_unallocated']/1000) 
             if 'tags' in node:
@@ -251,18 +247,17 @@ class SFAv1(BaseVersion):
         pass
 
     def add_links(self, links):
-        for link in links:
-            network_tag = self.xml.root
-            if link.component_manager_id != None:
-                network_hrn, type = urn_to_hrn(link.component_manager_id)
-                network_tag = self.add_network(network) 
+        networks = self.get_network_elements()
+        if len(networks) > 0:
+            xml = networks[0]
+        else:
+            xml = self.xml    
+        PGv2Link.add_links(xml, links)
 
-            link_elem = etree.SubElement(network_tag, 'link')
-            link_elem.set('endpoints', '%s %s' % (link.endpoint1.name, link.endpoint2.name))
-            description = etree.SubElement(link_elem, 'description').text = link.description
-            bw_unallocated = etree.SubElement(link_elem, 'bw_unallocated', units='kbps').text = link.capacity  
+    def add_link_requests(self, links):
+        PGv2Link.add_link_requests(self.xml, links)
 
-    def add_slivers(self, slivers, network=None, sliver_urn=None, no_dupes=False):
+    def add_slivers(self, slivers, network=None, sliver_urn=None, no_dupes=False, append=False):
         # add slice name to network tag
         network_tags = self.xml.xpath('//network')
         if network_tags:
@@ -283,10 +278,11 @@ class SFAv1(BaseVersion):
                     etree.SubElement(sliver_elem, tag['tagname']).text = value=tag['value']
             
         # remove all nodes without slivers
-        for node in nodes_without_slivers:
-            node_elem = self.get_node_element(node, network)
-            parent = node_elem.getparent()
-            parent.remove(node_elem)
+        if not append:
+            for node in nodes_without_slivers:
+                node_elem = self.get_node_element(node)
+                parent = node_elem.getparent()
+                parent.remove(node_elem)
 
     def remove_slivers(self, slivers, network=None, no_dupes=False):
         for sliver in slivers:
diff --git a/sfa/rspecs/xml_interface.py b/sfa/rspecs/xml_interface.py
deleted file mode 100755 (executable)
index 98fe416..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/usr/bin/python 
-from lxml import etree
-from StringIO import StringIO
-from datetime import datetime, timedelta
-from sfa.util.xrn import *
-from sfa.util.plxrn import hostname_to_urn
-from sfa.util.faults import SfaNotImplemented, InvalidRSpec, InvalidRSpecElement
-
-class XpathFilter:
-    @staticmethod
-    def xpath(filter={}):
-        xpath = ""
-        if filter:
-            filter_list = []
-            for (key, value) in filter.items():
-                if key == 'text':
-                    key = 'text()'
-                else:
-                    key = '@'+key
-                if isinstance(value, str):
-                    filter_list.append('%s="%s"' % (key, value))
-                elif isinstance(value, list):
-                    filter_list.append('contains("%s", %s)' % (' '.join(map(str, value)), key))
-            if filter_list:
-                xpath = ' and '.join(filter_list)
-                xpath = '[' + xpath + ']'
-        return xpath
-
-class XMLInterface:
-    def __init__(self, xml=""):
-        self.header = None 
-        self.template = None 
-        self.xml = None
-        self.namespaces = None
-        if xml:
-            self.parse_xml(xml)
-        else:
-            self.create()
-
-    def create(self):
-        """
-        Create root element
-        """
-        self.parse_rspec(self.template)
-    
-    def parse_xml(self, xml):
-        """
-        parse rspec into etree
-        """
-        parser = etree.XMLParser(remove_blank_text=True)
-        try:
-            tree = etree.parse(xml, parser)
-        except IOError:
-            # 'rspec' file doesnt exist. 'rspec' is proably an xml string
-            try:
-                tree = etree.parse(StringIO(xml), parser)
-            except Exception, e:
-                raise InvalidRSpec(str(e))
-        self.xml = tree.getroot()  
-
-    def validate(self, schema):
-        """
-        Validate against rng schema
-        """
-        relaxng_doc = etree.parse(schema)
-        relaxng = etree.RelaxNG(relaxng_doc)
-        if not relaxng(self.xml):
-            error = relaxng.error_log.last_error
-            message = "%s (line %s)" % (error.message, error.line)
-            raise InvalidRSpec(message)
-        return True
-
-    def xpath(self, xpath):
-        return self.xml.xpath(xpath, namespaces=self.namespaces)
-
-    def add_attribute(self, elem, name, value):
-        """
-        Add attribute to specified etree element    
-        """
-        opt = etree.SubElement(elem, name)
-        opt.text = value
-
-    def add_element(self, name, attrs={}, parent=None, text=""):
-        """
-        Generic wrapper around etree.SubElement(). Adds an element to 
-        specified parent node. Adds element to root node is parent is 
-        not specified. 
-        """
-        if parent == None:
-            parent = self.xml
-        element = etree.SubElement(parent, name)
-        if text:
-            element.text = text
-        if isinstance(attrs, dict):
-            for attr in attrs:
-                element.set(attr, attrs[attr])  
-        return element
-
-    def remove_attribute(self, elem, name, value):
-        """
-        Removes an attribute from an element
-        """
-        if elem is not None:
-            opts = elem.iterfind(name)
-            if opts is not None:
-                for opt in opts:
-                    if opt.text == value:
-                        elem.remove(opt)
-
-    def remove_element(self, element_name, root_node = None):
-        """
-        Removes all occurences of an element from the tree. Start at 
-        specified root_node if specified, otherwise start at tree's root.   
-        """
-        if not root_node:
-            root_node = self.xml
-
-        if not element_name.startswith('//'):
-            element_name = '//' + element_name
-
-        elements = root_node.xpath('%s ' % element_name, namespaces=self.namespaces)
-        for element in elements:
-            parent = element.getparent()
-            parent.remove(element)
-
-    def attributes_list(self, elem):
-        # convert a list of attribute tags into list of tuples
-        # (tagnme, text_value)
-        opts = []
-        if elem is not None:
-            for e in elem:
-                opts.append((e.tag, str(e.text).strip()))
-        return opts
-
-    def get_element_attributes(self, elem=None, depth=0):
-        if elem == None:
-            elem = self.root_node
-        if not hasattr(elem, 'attrib'):
-            # this is probably not an element node with attribute. could be just and an
-            # attribute, return it
-            return elem
-        attrs = dict(elem.attrib)
-        attrs['text'] = str(elem.text).strip()
-        attrs['parent'] = elem.getparent()
-        if isinstance(depth, int) and depth > 0:
-            for child_elem in list(elem):
-                key = str(child_elem.tag)
-                if key not in attrs:
-                    attrs[key] = [self.get_element_attributes(child_elem, depth-1)]
-                else:
-                    attrs[key].append(self.get_element_attributes(child_elem, depth-1))
-        else:
-            attrs['child_nodes'] = list(elem)
-        return attrs
-
-    def merge(self, in_xml):
-        pass
-
-    def cleanup(self):
-        """
-        Optional method which inheriting classes can choose to implent. 
-        """
-        pass 
-
-    def __str__(self):
-        return self.toxml()
-
-    def toxml(self, cleanup=False):
-        if cleanup:
-            self.cleanup()
-        return self.header + etree.tostring(self.xml, pretty_print=True)  
-        
-    def save(self, filename):
-        f = open(filename, 'w')
-        f.write(self.toxml())
-        f.close()
-if __name__ == '__main__':
-    rspec = RSpec('/tmp/resources.rspec')
-    print rspec
-
index 59a3e6b..e7340e1 100644 (file)
@@ -1,5 +1,4 @@
-from sfa.util.faults import *
-from sfa.util.server import SfaServer
+from sfa.server.sfaserver import SfaServer
 from sfa.util.xrn import hrn_to_urn
 from sfa.server.interface import Interfaces, Interface
 from sfa.util.config import Config     
index c83ac4a..3958c5f 100644 (file)
@@ -1,16 +1,12 @@
 #
 # Component is a SfaServer that implements the Component interface
 #
-### $Id: 
-### $URL: 
-#
-
 import tempfile
 import os
 import time
 import sys
 
-from sfa.util.componentserver import ComponentServer
+from sfa.server.sfaserver import SfaServer
  
 # GeniLight client support is optional
 try:
@@ -21,7 +17,8 @@ except ImportError:
 ##
 # Component is a SfaServer that serves component operations.
 
-class Component(ComponentServer):
+# set SFA_GENERIC_FLAVOUR=plcm to get a PlcComponentApi instance in the request handler 
+class Component(SfaServer):
     ##
     # Create a new registry object.
     #
@@ -31,5 +28,5 @@ class Component(ComponentServer):
     # @param cert_file certificate filename containing public key (could be a GID file)
 
     def __init__(self, ip, port, key_file, cert_file):
-        ComponentServer.__init__(self, ip, port, key_file, cert_file)
+        SfaServer.__init__(self, ip, port, key_file, cert_file)
         self.server.interface = 'component'
index baeb2e7..94302ec 100644 (file)
@@ -1,13 +1,6 @@
-import traceback
-import os.path
-
-from sfa.util.faults import *
-from sfa.util.storage import XmlStorage
-from sfa.util.xrn import get_authority, hrn_to_urn
-from sfa.util.record import SfaRecord
+#from sfa.util.faults import *
 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
-import sfa.util.soapprotocol as soapprotocol
-from sfa.trust.gid import GID
+from sfa.util.xml import XML
 
 # GeniLight client support is optional
 try:
@@ -15,10 +8,11 @@ try:
 except ImportError:
     GeniClientLight = None            
 
-
-
 class Interface:
-    
+    """
+    Interface to another SFA service, typically a peer, or the local aggregate
+    can retrieve a xmlrpclib.ServerProxy object for issuing calls there
+    """
     def __init__(self, hrn, addr, port, client_type='sfa'):
         self.hrn = hrn
         self.addr = addr
@@ -34,6 +28,7 @@ class Interface:
     def get_server(self, key_file, cert_file, timeout=30):
         server = None 
         if  self.client_type ==  'geniclientlight' and GeniClientLight:
+            # xxx url and self.api are undefined
             server = GeniClientLight(url, self.api.key_file, self.api.cert_file)
         else:
             server = xmlrpcprotocol.get_server(self.get_url(), key_file, cert_file, timeout) 
@@ -62,21 +57,20 @@ class Interfaces(dict):
     def __init__(self, conf_file):
         dict.__init__(self, {})
         # load config file
-        self.interface_info = XmlStorage(conf_file, self.default_dict)
-        self.interface_info.load()
-        records = self.interface_info.values()[0].values()[0]
-        if not isinstance(records, list):
-            records = [records]
-        
-        required_fields = self.default_fields.keys()
-        for record in records:
-            if not set(required_fields).issubset(record.keys()):
-                continue
-            # port is appended onto the domain, before the path. Should look like:
-            # http://domain:port/path
-            hrn, address, port = record['hrn'], record['addr'], record['port']
-            interface = Interface(hrn, address, port) 
-            self[hrn] = interface
+        required_fields = set(self.default_fields.keys())
+        self.interface_info = XML(conf_file).todict()
+        for value in self.interface_info.values():
+            if isinstance(value, list):
+                for record in value:
+                    if isinstance(record, dict) and \
+                      required_fields.issubset(record.keys()):
+                        hrn, address, port = record['hrn'], record['addr'], record['port']
+                        # sometime this is called at a very early stage with no config loaded
+                        # avoid to remember this instance in such a case
+                        if not address or not port:
+                            continue     
+                        interface = Interface(hrn, address, port)
+                        self[hrn] = interface   
 
     def get_server(self, hrn, key_file, cert_file, timeout=30):
         return self[hrn].get_server(key_file, cert_file, timeout)
index deaf89f..8638ce9 100755 (executable)
@@ -12,10 +12,10 @@ import traceback
 import xmlrpclib
 from mod_python import apache
 
-from sfa.plc.api import SfaAPI
+from sfa.plc.plcsfaapi import PlcSfaApi
 from sfa.util.sfalogging import logger
 
-api = SfaAPI(interface='aggregate')
+api = PlcSfaApi(interface='aggregate')
 
 def handler(req):
     try:
index 8879813..115fcba 100755 (executable)
@@ -12,10 +12,10 @@ import traceback
 import xmlrpclib
 from mod_python import apache
 
-from sfa.plc.api import SfaAPI
+from sfa.plc.plcsfaapi import PlcSfaApi
 from sfa.util.sfalogging import logger
 
-api = SfaAPI(interface='registry')
+api = PlcSfaApi(interface='registry')
 
 def handler(req):
     try:
index e0f2b92..3de4519 100755 (executable)
@@ -12,10 +12,10 @@ import traceback
 import xmlrpclib
 from mod_python import apache
 
-from sfa.plc.api import SfaAPI
+from sfa.plc.plcsfaapi import PlcSfaApi
 from sfa.util.sfalogging import logger
 
-api = SfaAPI(interface='slicemgr')
+api = PlcSfaApi(interface='slicemgr')
 
 def handler(req):
     try:
index 0536c5c..2a37c22 100644 (file)
@@ -1,13 +1,7 @@
 #
 # Registry is a SfaServer that implements the Registry interface
 #
-### $Id$
-### $URL$
-#
-
-from sfa.util.server import SfaServer
-from sfa.util.faults import *
-from sfa.util.xrn import hrn_to_urn
+from sfa.server.sfaserver import SfaServer
 from sfa.server.interface import Interfaces, Interface
 from sfa.util.config import Config 
 
index 8297b2d..0fbe140 100755 (executable)
 import os
 import sys
 from optparse import OptionParser
-from sfa.trust.certificate import Keypair, Certificate
+
+from sfa.util.config import Config
+from sfa.util.table import SfaTable
+
 from sfa.trust.gid import GID, create_uuid
 from sfa.trust.hierarchy import Hierarchy
-from sfa.util.config import Config
-from collections import defaultdict
 
 def main():
     args = sys.argv
@@ -110,7 +111,6 @@ def sign(options):
     
 
 def export_gid(options):
-    from sfa.util.table import SfaTable
     # lookup the record for the specified hrn 
     hrn = options.export
     type = options.type
@@ -124,7 +124,7 @@ def export_gid(options):
         # check the authorities hierarchy 
         hierarchy = Hierarchy()
         try:
-            auth_info = hierarchy.get_auth_info()
+            auth_info = hierarchy.get_auth_info(hrn)
             gid = auth_info.gid_object 
         except:
             print "Record: %s not found" % hrn
@@ -148,8 +148,6 @@ def import_gid(options):
     Import the specified gid into the registry (db and authorities 
     hierarchy) overwriting any previous gid.
     """
-    from sfa.util.table import SfaTable
-    from sfa.util.record import SfaRecord
     # load the gid
     gidfile = os.path.abspath(options.importgid)
     if not gidfile or not os.path.isfile(gidfile):
@@ -167,7 +165,7 @@ def import_gid(options):
     table = SfaTable()
     records = table.find({'hrn': gid.get_hrn(), 'type': 'authority'})
     if not records:
-        print "%s not found in record database" % get.get_hrn()  
+        print "%s not found in record database" % gid.get_hrn()  
         sys.exit(1)
 
     # update the database record
index f821f4c..93fef14 100644 (file)
@@ -5,7 +5,7 @@ import os
 import traceback
 from sfa.util.table import SfaTable
 from sfa.util.prefixTree import prefixTree
-from sfa.plc.api import SfaAPI
+from sfa.plc.plcsfaapi import PlcSfaApi
 from sfa.util.config import Config
 from sfa.trust.certificate import Keypair
 from sfa.trust.hierarchy import Hierarchy
@@ -31,7 +31,7 @@ def main():
     authority = config.SFA_INTERFACE_HRN
     url = 'http://%s:%s/' %(config.SFA_REGISTRY_HOST, config.SFA_REGISTRY_PORT)
     registry = xmlrpcprotocol.get_server(url, key_file, cert_file)
-    sfa_api = SfaAPI(key_file = key_file, cert_file = cert_file, interface='registry')
+    sfa_api = PlcSfaApi(key_file = key_file, cert_file = cert_file, interface='registry')
     credential = sfa_api.getCredential()
 
     # get peer registries
similarity index 88%
rename from sfa/server/sfa-server.py
rename to sfa/server/sfa-start.py
index 4ed8c1f..966a13e 100755 (executable)
@@ -1,13 +1,12 @@
 #!/usr/bin/python
 #
-# SFA PLC Wrapper
+# PlanetLab SFA implementation
 #
-# This wrapper implements the SFA Registry and Slice Interfaces on PLC.
+# This implements the SFA Registry and Slice Interfaces on PLC.
 # Depending on command line options, it starts some combination of a
 # Registry, an Aggregate Manager, and a Slice Manager.
 #
-# There are several items that need to be done before starting the wrapper
-# server.
+# There are several items that need to be done before starting the servers.
 #
 # NOTE:  Many configuration settings, including the PLC maintenance account
 # credentials, URI of the PLCAPI, and PLC DB URI and admin credentials are initialized
@@ -43,7 +42,7 @@ from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.hierarchy import Hierarchy
 from sfa.trust.gid import GID
 from sfa.util.config import Config
-from sfa.plc.api import SfaAPI
+from sfa.plc.plcsfaapi import PlcSfaApi
 from sfa.server.registry import Registries
 from sfa.server.aggregate import Aggregates
 from sfa.util.xrn import get_authority, hrn_to_urn
@@ -135,34 +134,6 @@ def init_self_signed_cert(hrn, key, server_cert_file):
     cert.sign()
     cert.save_to_file(server_cert_file)
 
-def init_server(options, config):
-    """
-    Execute the init method defined in the manager file 
-    """
-    def init_manager(manager_module, manager_base):
-        try: manager = __import__(manager_module, fromlist=[manager_base])
-        except: manager = None
-        if manager and hasattr(manager, 'init_server'):
-            manager.init_server()
-    
-    manager_base = 'sfa.managers'
-    if options.registry:
-        mgr_type = config.SFA_REGISTRY_TYPE
-        manager_module = manager_base + ".registry_manager_%s" % mgr_type
-        init_manager(manager_module, manager_base)    
-    if options.am:
-        mgr_type = config.SFA_AGGREGATE_TYPE
-        manager_module = manager_base + ".aggregate_manager_%s" % mgr_type
-        init_manager(manager_module, manager_base)    
-    if options.sm:
-        mgr_type = config.SFA_SM_TYPE
-        manager_module = manager_base + ".slice_manager_%s" % mgr_type
-        init_manager(manager_module, manager_base)    
-    if options.cm:
-        mgr_type = config.SFA_CM_TYPE
-        manager_module = manager_base + ".component_manager_%s" % mgr_type
-        init_manager(manager_module, manager_base)    
-
 def install_peer_certs(server_key_file, server_cert_file):
     """
     Attempt to install missing trusted gids and db records for 
@@ -172,7 +143,7 @@ def install_peer_certs(server_key_file, server_cert_file):
     # There should be a gid file in /etc/sfa/trusted_roots for every
     # peer registry found in in the registries.xml config file. If there
     # are any missing gids, request a new one from the peer registry.
-    api = SfaAPI(key_file = server_key_file, cert_file = server_cert_file)
+    api = PlcSfaApi(key_file = server_key_file, cert_file = server_cert_file)
     registries = Registries()
     aggregates = Aggregates()
     interfaces = dict(registries.items() + aggregates.items())
@@ -229,7 +200,7 @@ def update_cert_records(gids):
     Make sure there is a record in the registry for the specified gids. 
     Removes old records from the db.
     """
-    # import SfaTable here so this module can be loaded by ComponentAPI
+    # import SfaTable here so this module can be loaded by PlcComponentApi
     from sfa.util.table import SfaTable
     from sfa.util.record import SfaRecord
     if not gids:
@@ -261,7 +232,7 @@ def update_cert_records(gids):
         
 def main():
     # Generate command line parser
-    parser = OptionParser(usage="sfa-server [options]")
+    parser = OptionParser(usage="sfa-start.py [options]")
     parser.add_option("-r", "--registry", dest="registry", action="store_true",
          help="run registry server", default=False)
     parser.add_option("-s", "--slicemgr", dest="sm", action="store_true",
@@ -285,8 +256,7 @@ def main():
     server_cert_file = os.path.join(hierarchy.basedir, "server.cert")
 
     init_server_key(server_key_file, server_cert_file, config, hierarchy)
-    init_server(options, config)
+
     if (options.daemon):  daemon()
     
     if options.trusted_certs:
index b0a5a47..954ff6d 100755 (executable)
@@ -4,10 +4,11 @@ import os
 import tempfile
 from optparse import OptionParser
 
-from sfa.util.faults import *
+from sfa.util.faults import ConnectionKeyGIDMismatch
 from sfa.util.config import Config
 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
 from sfa.util.plxrn import hrn_to_pl_slicename, slicename_to_hrn
+
 from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.credential import Credential
 from sfa.trust.gid import GID
@@ -221,8 +222,8 @@ def get_gids(registry=None, verbose=False):
     if verbose:
         print "Getting current slices on this node"
     # get a list of slices on this node
-    from sfa.plc.api import ComponentAPI
-    api = ComponentAPI()
+    from sfa.plc.plcsfaapi import PlcComponentApi
+    api = PlcComponentApi()
     xids_tuple = api.nodemanager.GetXIDs()
     slices = eval(xids_tuple[1])
     slicenames = slices.keys()
diff --git a/sfa/server/sfaapi.py b/sfa/server/sfaapi.py
new file mode 100644 (file)
index 0000000..9d22e7f
--- /dev/null
@@ -0,0 +1,209 @@
+import os.path
+import datetime
+
+from sfa.util.faults import SfaAPIError
+from sfa.util.config import Config
+from sfa.util.cache import Cache
+from sfa.trust.auth import Auth
+from sfa.trust.certificate import Keypair, Certificate
+from sfa.trust.credential import Credential
+from sfa.trust.rights import determine_rights
+
+# this is wrong all right, but temporary, will use generic
+from sfa.server.xmlrpcapi import XmlrpcApi
+import os
+import datetime
+
+####################
+class SfaApi (XmlrpcApi): 
+    
+    """
+    An SfaApi instance is a basic xmlrcp service
+    augmented with the local cryptographic material and hrn
+
+    It also has the notion of its own interface (a string describing
+    whether we run a registry, aggregate or slicemgr) and has 
+    the notion of neighbour sfa services as defined 
+    in /etc/sfa/{aggregates,registries}.xml
+
+    Finally it contains a cache instance
+
+    It gets augmented by the generic layer with 
+    (*) an instance of manager (actually a manager module for now)
+    (*) which in turn holds an instance of a testbed driver
+    For convenience api.manager.driver == api.driver
+    """
+
+    def __init__ (self, encoding="utf-8", methods='sfa.methods', 
+                  config = "/etc/sfa/sfa_config.py", 
+                  peer_cert = None, interface = None, 
+                  key_file = None, cert_file = None, cache = None):
+        
+        XmlrpcApi.__init__ (self, encoding)
+        
+        # we may be just be documenting the API
+        if config is None:
+            return
+        # Load configuration
+        self.config = Config(config)
+        self.credential = None
+        self.auth = Auth(peer_cert)
+        self.interface = interface
+        self.hrn = self.config.SFA_INTERFACE_HRN
+        self.key_file = key_file
+        self.key = Keypair(filename=self.key_file)
+        self.cert_file = cert_file
+        self.cert = Certificate(filename=self.cert_file)
+        self.cache = cache
+        if self.cache is None:
+            self.cache = Cache()
+
+        # load registries
+        from sfa.server.registry import Registries
+        self.registries = Registries() 
+
+        # load aggregates
+        from sfa.server.aggregate import Aggregates
+        self.aggregates = Aggregates()
+        
+        # filled later on by generic/Generic
+        self.manager=None
+
+    # tmp
+    def get_interface_manager(self, manager_base = 'sfa.managers'):
+        return self.manager
+
+    def get_server(self, interface, cred, timeout=30):
+        """
+        Returns a connection to the specified interface. Use the specified
+        credential to determine the caller and look for the caller's key/cert 
+        in the registry hierarchy cache. 
+        """       
+        from sfa.trust.hierarchy import Hierarchy
+        if not isinstance(cred, Credential):
+            cred_obj = Credential(string=cred)
+        else:
+            cred_obj = cred
+        caller_gid = cred_obj.get_gid_caller()
+        hierarchy = Hierarchy()
+        auth_info = hierarchy.get_auth_info(caller_gid.get_hrn())
+        key_file = auth_info.get_privkey_filename()
+        cert_file = auth_info.get_gid_filename()
+        server = interface.get_server(key_file, cert_file, timeout)
+        return server
+               
+        
+    def getCredential(self):
+        """
+        Return a valid credential for this interface. 
+        """
+        type = 'authority'
+        path = self.config.SFA_DATA_DIR
+        filename = ".".join([self.interface, self.hrn, type, "cred"])
+        cred_filename = os.path.join(path,filename)
+        cred = None
+        if os.path.isfile(cred_filename):
+            cred = Credential(filename = cred_filename)
+            # make sure cred isnt expired
+            if not cred.get_expiration or \
+               datetime.datetime.utcnow() < cred.get_expiration():    
+                return cred.save_to_string(save_parents=True)
+
+        # get a new credential
+        if self.interface in ['registry']:
+            cred =  self.__getCredentialRaw()
+        else:
+            cred =  self.__getCredential()
+        cred.save_to_file(cred_filename, save_parents=True)
+
+        return cred.save_to_string(save_parents=True)
+
+
+    def getDelegatedCredential(self, creds):
+        """
+        Attempt to find a credential delegated to us in
+        the specified list of creds.
+        """
+        from sfa.trust.hierarchy import Hierarchy
+        if creds and not isinstance(creds, list): 
+            creds = [creds]
+        hierarchy = Hierarchy()
+                
+        delegated_cred = None
+        for cred in creds:
+            if hierarchy.auth_exists(Credential(string=cred).get_gid_caller().get_hrn()):
+                delegated_cred = cred
+                break
+        return delegated_cred
+    def __getCredential(self):
+        """ 
+        Get our credential from a remote registry 
+        """
+        from sfa.server.registry import Registries
+        registries = Registries()
+        registry = registries.get_server(self.hrn, self.key_file, self.cert_file)
+        cert_string=self.cert.save_to_string(save_parents=True)
+        # get self credential
+        self_cred = registry.GetSelfCredential(cert_string, self.hrn, 'authority')
+        # get credential
+        cred = registry.GetCredential(self_cred, self.hrn, 'authority')
+        return Credential(string=cred)
+
+    def __getCredentialRaw(self):
+        """
+        Get our current credential directly from the local registry.
+        """
+
+        hrn = self.hrn
+        auth_hrn = self.auth.get_authority(hrn)
+    
+        # is this a root or sub authority
+        if not auth_hrn or hrn == self.config.SFA_INTERFACE_HRN:
+            auth_hrn = hrn
+        auth_info = self.auth.get_auth_info(auth_hrn)
+        table = self.SfaTable()
+        records = table.findObjects({'hrn': hrn, 'type': 'authority+sa'})
+        if not records:
+            raise RecordNotFound
+        record = records[0]
+        type = record['type']
+        object_gid = record.get_gid_object()
+        new_cred = Credential(subject = object_gid.get_subject())
+        new_cred.set_gid_caller(object_gid)
+        new_cred.set_gid_object(object_gid)
+        new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
+        
+        r1 = determine_rights(type, hrn)
+        new_cred.set_privileges(r1)
+        new_cred.encode()
+        new_cred.sign()
+
+        return new_cred
+   
+    def loadCredential (self):
+        """
+        Attempt to load credential from file if it exists. If it doesnt get
+        credential from registry.
+        """
+
+        # see if this file exists
+        # XX This is really the aggregate's credential. Using this is easier than getting
+        # the registry's credential from iteslf (ssl errors).
+        filename = self.interface + self.hrn + ".ma.cred"
+        ma_cred_path = os.path.join(self.config.SFA_DATA_DIR,filename)
+        try:
+            self.credential = Credential(filename = ma_cred_path)
+        except IOError:
+            self.credential = self.getCredentialFromRegistry()
+
+    def get_cached_server_version(self, server):
+        cache_key = server.url + "-version"
+        server_version = None
+        if self.cache:
+            server_version = self.cache.get(cache_key)
+        if not server_version:
+            server_version = server.GetVersion()
+            # cache version for 24 hours
+            self.cache.add(cache_key, server_version, ttl= 60*60*24)
+        return server_version
diff --git a/sfa/server/sfaserver.py b/sfa/server/sfaserver.py
new file mode 100644 (file)
index 0000000..f392b78
--- /dev/null
@@ -0,0 +1,65 @@
+##
+# This module implements a general-purpose server layer for sfa.
+# The same basic server should be usable on the registry, component, or
+# other interfaces.
+#
+# TODO: investigate ways to combine this with existing PLC server?
+##
+
+import threading
+
+from sfa.server.threadedserver import ThreadedServer, SecureXMLRpcRequestHandler
+
+from sfa.util.sfalogging import logger
+from sfa.trust.certificate import Keypair, Certificate
+
+##
+# Implements an HTTPS XML-RPC server. Generally it is expected that SFA
+# functions will take a credential string, which is passed to
+# decode_authentication. Decode_authentication() will verify the validity of
+# the credential, and verify that the user is using the key that matches the
+# GID supplied in the credential.
+
+class SfaServer(threading.Thread):
+
+    ##
+    # Create a new SfaServer object.
+    #
+    # @param ip the ip address to listen on
+    # @param port the port to listen on
+    # @param key_file private key filename of registry
+    # @param cert_file certificate filename containing public key 
+    #   (could be a GID file)
+
+    def __init__(self, ip, port, key_file, cert_file,interface):
+        threading.Thread.__init__(self)
+        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.interface=interface
+        self.trusted_cert_list = None
+        self.register_functions()
+        logger.info("Starting SfaServer, interface=%s"%interface)
+
+    ##
+    # Register functions that will be served by the XMLRPC server. This
+    # function should be overridden by each descendant class.
+
+    def register_functions(self):
+        self.server.register_function(self.noop)
+
+    ##
+    # Sample no-op server function. The no-op function decodes the credential
+    # that was passed to it.
+
+    def noop(self, cred, anything):
+        return anything
+
+    ##
+    # Execute the server, serving requests forever. 
+
+    def run(self):
+        self.server.serve_forever()
+
+
index 8842eae..9a7fa4a 100644 (file)
@@ -1,11 +1,8 @@
-### $Id$
-### $URL$
-
 import os
 import sys
 import datetime
 import time
-from sfa.util.server import *
+from sfa.server.sfaserver import SfaServer
 
 class SliceMgr(SfaServer):
 
similarity index 80%
rename from sfa/util/server.py
rename to sfa/server/threadedserver.py
index c3ae718..7a9c368 100644 (file)
@@ -7,24 +7,23 @@
 ##
 
 import sys
-import socket, os
+import socket
 import traceback
 import threading
 from Queue import Queue
 import SocketServer
 import BaseHTTPServer
-import SimpleHTTPServer
 import SimpleXMLRPCServer
 from OpenSSL import SSL
 
-from sfa.trust.certificate import Keypair, Certificate
-from sfa.trust.trustedroots import TrustedRoots
+from sfa.util.sfalogging import logger
 from sfa.util.config import Config
-from sfa.trust.credential import *
-from sfa.util.faults import *
-from sfa.plc.api import SfaAPI
 from sfa.util.cache import Cache 
-from sfa.util.sfalogging import logger
+from sfa.trust.certificate import Certificate
+from sfa.trust.trustedroots import TrustedRoots
+
+# don't hard code an api class anymore here
+from sfa.generic import Generic
 
 ##
 # Verification callback for pyOpenSSL. We do our own checking of keys because
@@ -97,11 +96,18 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
         try:
             peer_cert = Certificate()
             peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate())
-            self.api = SfaAPI(peer_cert = peer_cert, 
-                              interface = self.server.interface, 
-                              key_file = self.server.key_file, 
-                              cert_file = self.server.cert_file,
-                              cache = self.cache)
+            generic=Generic.the_flavour()
+            self.api = generic.make_api (peer_cert = peer_cert, 
+                                         interface = self.server.interface, 
+                                         key_file = self.server.key_file, 
+                                         cert_file = self.server.cert_file,
+                                         cache = self.cache)
+            #logger.info("SecureXMLRpcRequestHandler.do_POST:")
+            #logger.info("interface=%s"%self.server.interface)
+            #logger.info("key_file=%s"%self.server.key_file)
+            #logger.info("api=%s"%self.api)
+            #logger.info("server=%s"%self.server)
+            #logger.info("handler=%s"%self)
             # get arguments
             request = self.rfile.read(int(self.headers["content-length"]))
             remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
@@ -129,6 +135,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
 ##
 # Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
 class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
+
     def __init__(self, server_address, HandlerClass, key_file, cert_file, logRequests=True):
         """Secure XML-RPC server.
 
@@ -260,54 +267,3 @@ class ThreadPoolMixIn(SocketServer.ThreadingMixIn):
 
 class ThreadedServer(ThreadPoolMixIn, SecureXMLRPCServer):
     pass
-##
-# Implements an HTTPS XML-RPC server. Generally it is expected that SFA
-# functions will take a credential string, which is passed to
-# decode_authentication. Decode_authentication() will verify the validity of
-# the credential, and verify that the user is using the key that matches the
-# GID supplied in the credential.
-
-class SfaServer(threading.Thread):
-
-    ##
-    # Create a new SfaServer object.
-    #
-    # @param ip the ip address to listen on
-    # @param port the port to listen on
-    # @param key_file private key filename of registry
-    # @param cert_file certificate filename containing public key 
-    #   (could be a GID file)
-
-    def __init__(self, ip, port, key_file, cert_file,interface):
-        threading.Thread.__init__(self)
-        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.interface=interface
-        self.trusted_cert_list = None
-        self.register_functions()
-        logger.info("Starting SfaServer, interface=%s"%interface)
-
-    ##
-    # Register functions that will be served by the XMLRPC server. This
-    # function should be overridden by each descendant class.
-
-    def register_functions(self):
-        self.server.register_function(self.noop)
-
-    ##
-    # Sample no-op server function. The no-op function decodes the credential
-    # that was passed to it.
-
-    def noop(self, cred, anything):
-        self.decode_authentication(cred)
-        return anything
-
-    ##
-    # Execute the server, serving requests forever. 
-
-    def run(self):
-        self.server.serve_forever()
-
-
diff --git a/sfa/server/xmlrpcapi.py b/sfa/server/xmlrpcapi.py
new file mode 100644 (file)
index 0000000..456cd42
--- /dev/null
@@ -0,0 +1,184 @@
+#
+# SFA XML-RPC and SOAP interfaces
+#
+
+import string
+import xmlrpclib
+
+# SOAP support is optional
+try:
+    import SOAPpy
+    from SOAPpy.Parser import parseSOAPRPC
+    from SOAPpy.Types import faultType
+    from SOAPpy.NS import NS
+    from SOAPpy.SOAPBuilder import buildSOAP
+except ImportError:
+    SOAPpy = None
+
+####################
+#from sfa.util.faults import SfaNotImplemented, SfaAPIError, SfaInvalidAPIMethod, SfaFault
+from sfa.util.faults import SfaInvalidAPIMethod, SfaAPIError, SfaFault
+from sfa.util.sfalogging import logger
+
+####################
+# See "2.2 Characters" in the XML specification:
+#
+# #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
+# avoiding
+# [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
+
+invalid_xml_ascii = map(chr, range(0x0, 0x8) + [0xB, 0xC] + range(0xE, 0x1F))
+xml_escape_table = string.maketrans("".join(invalid_xml_ascii), "?" * len(invalid_xml_ascii))
+
+def xmlrpclib_escape(s, replace = string.replace):
+    """
+    xmlrpclib does not handle invalid 7-bit control characters. This
+    function augments xmlrpclib.escape, which by default only replaces
+    '&', '<', and '>' with entities.
+    """
+
+    # This is the standard xmlrpclib.escape function
+    s = replace(s, "&", "&amp;")
+    s = replace(s, "<", "&lt;")
+    s = replace(s, ">", "&gt;",)
+
+    # Replace invalid 7-bit control characters with '?'
+    return s.translate(xml_escape_table)
+
+def xmlrpclib_dump(self, value, write):
+    """
+    xmlrpclib cannot marshal instances of subclasses of built-in
+    types. This function overrides xmlrpclib.Marshaller.__dump so that
+    any value that is an instance of one of its acceptable types is
+    marshalled as that type.
+
+    xmlrpclib also cannot handle invalid 7-bit control characters. See
+    above.
+    """
+
+    # Use our escape function
+    args = [self, value, write]
+    if isinstance(value, (str, unicode)):
+        args.append(xmlrpclib_escape)
+
+    try:
+        # Try for an exact match first
+        f = self.dispatch[type(value)]
+    except KeyError:
+        raise
+        # Try for an isinstance() match
+        for Type, f in self.dispatch.iteritems():
+            if isinstance(value, Type):
+                f(*args)
+                return
+        raise TypeError, "cannot marshal %s objects" % type(value)
+    else:
+        f(*args)
+
+# You can't hide from me!
+xmlrpclib.Marshaller._Marshaller__dump = xmlrpclib_dump
+
+class XmlrpcApi:
+    """
+    The XmlrpcApi class implements a basic xmlrpc (or soap) service 
+    """
+
+    protocol = None
+  
+    def __init__ (self, encoding="utf-8", methods='sfa.methods'):
+
+        self.encoding = encoding
+        self.source = None 
+        
+        # flat list of method names
+        self.methods_module = methods_module = __import__(methods, fromlist=[methods])
+        self.methods = methods_module.all
+
+        self.logger = logger
+    def callable(self, method):
+        """
+        Return a new instance of the specified method.
+        """
+        # Look up method
+        if method not in self.methods:
+            raise SfaInvalidAPIMethod, method
+        
+        # Get new instance of method
+        try:
+            classname = method.split(".")[-1]
+            module = __import__(self.methods_module.__name__ + "." + method, globals(), locals(), [classname])
+            callablemethod = getattr(module, classname)(self)
+            return getattr(module, classname)(self)
+        except (ImportError, AttributeError):
+            raise SfaInvalidAPIMethod, method
+
+    def call(self, source, method, *args):
+        """
+        Call the named method from the specified source with the
+        specified arguments.
+        """
+        function = self.callable(method)
+        function.source = source
+        self.source = source
+        return function(*args)
+
+    
+    def handle(self, source, data, method_map):
+        """
+        Handle an XML-RPC or SOAP request from the specified source.
+        """
+        # Parse request into method name and arguments
+        try:
+            interface = xmlrpclib
+            self.protocol = 'xmlrpclib'
+            (args, method) = xmlrpclib.loads(data)
+            if method_map.has_key(method):
+                method = method_map[method]
+            methodresponse = True
+            
+        except Exception, e:
+            if SOAPpy is not None:
+                self.protocol = 'soap'
+                interface = SOAPpy
+                (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
+                method = r._name
+                args = r._aslist()
+                # XXX Support named arguments
+            else:
+                raise e
+
+        try:
+            result = self.call(source, method, *args)
+        except SfaFault, fault:
+            result = fault 
+        except Exception, fault:
+            self.logger.log_exc("XmlrpcApi.handle has caught Exception")
+            result = SfaAPIError(fault)
+
+
+        # Return result
+        response = self.prepare_response(result, method)
+        return response
+    
+    def prepare_response(self, result, method=""):
+        """
+        convert result to a valid xmlrpc or soap response
+        """   
+        if self.protocol == 'xmlrpclib':
+            if not isinstance(result, SfaFault):
+                result = (result,)
+            response = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
+        elif self.protocol == 'soap':
+            if isinstance(result, Exception):
+                result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
+                result._setDetail("Fault %d: %s" % (result.faultCode, result.faultString))
+            else:
+                response = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)
+        else:
+            if isinstance(result, Exception):
+                raise result 
+            
+        return response
+
index de0af99..43af740 100644 (file)
@@ -3,16 +3,20 @@
 #
 import sys
 
+from sfa.util.faults import InsufficientRights, MissingCallerGID, MissingTrustedRoots, PermissionError, \
+    BadRequestHash, ConnectionKeyGIDMismatch, SfaPermissionDenied
+from sfa.util.sfalogging import logger
+from sfa.util.config import Config
+from sfa.util.xrn import get_authority
+
+from sfa.trust.gid import GID
+from sfa.trust.rights import Rights
 from sfa.trust.certificate import Keypair, Certificate
 from sfa.trust.credential import Credential
 from sfa.trust.trustedroots import TrustedRoots
-from sfa.util.faults import *
 from sfa.trust.hierarchy import Hierarchy
-from sfa.util.config import *
-from sfa.util.xrn import get_authority
-from sfa.util.sfaticket import *
+from sfa.trust.sfaticket import SfaTicket
 
-from sfa.util.sfalogging import logger
 
 class Auth:
     """
@@ -145,7 +149,8 @@ class Auth:
 
     def authenticateCert(self, certStr, requestHash):
         cert = Certificate(string=certStr)
-        self.validateCert(self, cert)   
+        # xxx should be validateCred ??
+        self.validateCred(cert)   
 
     def gidNoop(self, gidStr, value, requestHash):
         self.authenticateGid(gidStr, [gidStr, value], requestHash)
@@ -313,7 +318,7 @@ class Auth:
         if not isinstance(creds, list):
             creds = [creds]
         creds = []
-        if not isinistance(caller_hrn_list, list):
+        if not isinstance(caller_hrn_list, list):
             caller_hrn_list = [caller_hrn_list]
         for cred in creds:
             try:
index bcec9d6..f0a2d71 100644 (file)
-#----------------------------------------------------------------------\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
-import traceback\r
-from tempfile import mkstemp\r
-\r
-from OpenSSL import crypto\r
-import M2Crypto\r
-from M2Crypto import X509\r
-\r
-from sfa.util.sfalogging import logger\r
-from sfa.util.xrn import urn_to_hrn\r
-from sfa.util.faults import *\r
-from sfa.util.sfalogging import logger\r
-\r
-glo_passphrase_callback = None\r
-\r
-##\r
-# A global callback msy 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
-        return None\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
-        return None\r
-\r
-    k = Keypair()\r
-    try:\r
-        k.load_pubkey_from_file(ssl_fn)\r
-    except:\r
-        logger.log_exc("convert_public_key caught exception")\r
-        k = None\r
-\r
-    # remove the temporary files\r
-    os.remove(ssh_fn)\r
-    os.remove(ssl_fn)\r
-\r
-    return k\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 "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 not trusted by any of %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"%self.get_printable_subject(), self.parent.get_printable_subject(), self.get_issuer())\r
-            raise CertNotSignedByParent(self.get_printable_subject() + ": Parent %s, issuer %s" % (self.parent.get_printable_subject(), 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" % (self.get_printable_subject(), self.parent.get_printable_subject()))\r
-            raise CertNotSignedByParent(self.get_printable_subject() + ": Parent %s not a CA" % self.parent.get_printable_subject())\r
-\r
-        # if the parent isn't verified...\r
-        logger.debug("verify_chain: .. %s, -> verifying parent %s"%(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
+#----------------------------------------------------------------------
+# 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:
+        return None
+
+    (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):
+        return None
+
+    k = Keypair()
+    try:
+        k.load_pubkey_from_file(ssl_fn)
+    except:
+        logger.log_exc("convert_public_key caught exception")
+        k = None
+
+    # remove the temporary files
+    os.remove(ssh_fn)
+    os.remove(ssl_fn)
+
+    return k
+
+##
+# 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
+&