Merge branch 'master' of ssh://bakers@git.planet-lab.org/git/sfa
authorsmbaker <smbaker@fc8clean.lan>
Mon, 10 Oct 2011 23:05:24 +0000 (16:05 -0700)
committersmbaker <smbaker@fc8clean.lan>
Mon, 10 Oct 2011 23:05:24 +0000 (16:05 -0700)
27 files changed:
Makefile
sfa/client/client_helper.py [new file with mode: 0644]
sfa/client/sfi.py
sfa/client/sfiAddAttribute.py
sfa/client/sfiAddSliver.py
sfa/client/sfiDeleteAttribute.py
sfa/client/sfiDeleteSliver.py
sfa/client/sfiListNodes.py
sfa/client/sfiListSlivers.py
sfa/managers/aggregate_manager_eucalyptus.py
sfa/managers/aggregate_manager_pl.py
sfa/managers/slice_manager_pl.py
sfa/plc/aggregate.py
sfa/rspecs/pg_rspec.py [deleted file]
sfa/rspecs/pg_rspec_converter.py
sfa/rspecs/rspec.py
sfa/rspecs/rspec_converter.py
sfa/rspecs/rspec_parser.py [deleted file]
sfa/rspecs/rspec_version.py [changed mode: 0755->0644]
sfa/rspecs/sfa_rspec.py [deleted file]
sfa/rspecs/sfa_rspec_converter.py
sfa/rspecs/version_manager.py
sfa/rspecs/versions/pgv2.py
sfa/rspecs/versions/pgv3.py [new file with mode: 0644]
sfa/rspecs/versions/sfav1.py
sfa/rspecs/xml.py [new file with mode: 0755]
sfa/rspecs/xml_interface.py

index 5bb1bba..f162f5f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ uninstall: python-uninstall tests-uninstall
 
 .PHONY: all install clean uninstall
 
-VERSIONTAG=should-be-redefined-by-specfile
+VERSIONTAG=0.0-0-should.be-redefined-by-specfile
 SCMURL=should-be-redefined-by-specfile
 
 ##########
diff --git a/sfa/client/client_helper.py b/sfa/client/client_helper.py
new file mode 100644 (file)
index 0000000..64c8b43
--- /dev/null
@@ -0,0 +1,37 @@
+
+def pg_users_arg(records):
+    users = []  
+    for record in records:
+        if record['type'] != 'user': 
+            continue
+        user = {'urn': record['geni_urn'],
+                'keys': record['keys']}
+        users.append(user)
+    return users    
+
+def sfa_users_arg(records, slice_record):
+    users = []
+    for record in records:
+        if record['type'] != 'user': 
+            continue
+        user = {'urn': record['geni_urn'], #
+                'keys': record['keys'],
+                'email': record['email'], #  needed for MyPLC
+                'person_id': record['person_id'], # needed for MyPLC
+                'first_name': record['first_name'], # needed for MyPLC
+                'last_name': record['last_name'], # needed for MyPLC
+                'slice_record': slice_record, # needed for legacy refresh peer
+                'key_ids': record['key_ids'] # needed for legacy refresh peer
+                }         
+        users.append(user)
+    return users        
+
+def sfa_to_pg_users_arg(users):
+
+    new_users = []
+    fields = ['urn', 'keys']
+    for user in users:
+        new_user = dict([item for item in user.items() \
+          if item[0] in fields])
+        new_users.append(new_user)
+    return new_users        
index 72a6f86..6fd0a70 100755 (executable)
@@ -17,20 +17,21 @@ from lxml import etree
 from StringIO import StringIO
 from types import StringTypes, ListType
 from optparse import OptionParser
-
+from sfa.client.client_helper import pg_users_arg, sfa_users_arg, sfa_to_pg_users_arg 
 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.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.rspec_converter import RSpecConverter
 from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn
 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
 from sfa.util.config import Config
 from sfa.util.version import version_core
 from sfa.util.cache import Cache
-from sfa.rspecs.rspec_version import RSpecVersion
-from sfa.rspecs.pg_rspec import pg_rspec_request_version
+from sfa.rspecs.version_manager import VersionManager
 
 AGGREGATE_PORT=12346
 CM_PORT=12346
@@ -437,7 +438,7 @@ class Sfi:
         Returns true if server support the optional call_id arg, false otherwise. 
         """
         server_version = self.get_cached_server_version(server)
-        if 'sfa' in server_version:
+        if 'sfa' in server_version and 'code_tag' in server_version:
             code_tag = server_version['code_tag']
             code_tag_parts = code_tag.split("-")
             
@@ -956,14 +957,15 @@ class Sfi:
             delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
             creds.append(delegated_cred)
         if opts.rspec_version:
+            version_manager = VersionManager()
             server_version = self.get_cached_server_version(server)
             if 'sfa' in server_version:
                 # just request the version the client wants 
-                call_options['rspec_version'] = dict(RSpecVersion(opts.rspec_version)) 
+                call_options['rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
             else:
                 # this must be a protogeni aggregate. We should request a v2 ad rspec
                 # regardless of what the client user requested 
-                call_options['rspec_version'] = dict(pg_rspec_request_version)     
+                call_options['rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()     
         #panos add info options
         if opts.info:
             call_options['info'] = opts.info 
@@ -999,41 +1001,24 @@ class Sfi:
         #    keys: [<ssh key A>, <ssh key B>] 
         #  }]
         users = []
-        all_keys = []
-        all_key_ids = []
         slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
         if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
             slice_record = slice_records[0]
             user_hrns = slice_record['researcher']
             user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
             user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
-            for user_record in user_records:
-                if user_record['type'] != 'user':
-                    continue
-                #user = {'urn': user_cred.get_gid_caller().get_urn(),'keys': []}
-                user = {'urn': user_cred.get_gid_caller().get_urn(), #
-                        'keys': user_record['keys'],
-                        'email': user_record['email'], #  needed for MyPLC
-                        'person_id': user_record['person_id'], # needed for MyPLC
-                        'first_name': user_record['first_name'], # needed for MyPLC
-                        'last_name': user_record['last_name'], # needed for MyPLC
-                        'slice_record': slice_record, # needed for legacy refresh peer
-                        'key_ids': user_record['key_ids'] # needed for legacy refresh peer
-                } 
-                users.append(user)
-                all_keys.extend(user_record['keys'])
-                all_key_ids.extend(user_record['key_ids'])
-            # ProtoGeni Aggregates will only install the keys of the user that is issuing the
-            # request. So we will add all to the current caller's list of keys
+            
             if 'sfa' not in server_version:
-                for user in users:
-                    if user['urn'] == user_cred.get_gid_caller().get_urn():
-                        user['keys'] = all_keys  
-
+                users = pg_users_arg(user_records)
+                rspec = RSpec(rspec)
+                rspec.filter({'component_manager_id': server_version['urn']})
+                rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
+            else:
+                users = sfa_users_arg(user_records, slice_record)
         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 0242a13..9c2eae5 100755 (executable)
@@ -3,7 +3,7 @@
 import sys
 from sfa.util.rspecHelper import RSpec, Commands
 from sfa.client.sfi_commands import Commands
-from sfa.rspecs.rspec_parser import parse_rspec
+from sfa.rspecs.rspec import RSpec
 
 command = Commands(usage="%prog [options] [node1 node2...]",
                    description="Add sliver attributes to the RSpec. " +
@@ -19,7 +19,7 @@ command.prep()
 
 if command.opts.infile:
     attrs = command.get_attribute_dict()
-    rspec = parse_rspec(command.opts.infile)
+    rspec = RSpec(command.opts.infile)
     nodes = []
     if command.opts.nodefile:
         f = open(command.opts.nodefile, "r")
@@ -32,13 +32,13 @@ if command.opts.infile:
         for value in attrs[name]:
             if not nodes:
                 try:
-                    rspec.add_default_sliver_attribute(name, value)
+                    rspec.version.add_default_sliver_attribute(name, value)
                 except:
                     print >> sys.stderr, "FAILED: on all nodes: %s=%s" % (name, value)
             else:
                 for node in nodes:
                     try:
-                        rspec.add_sliver_attribute(node, name, value)
+                        rspec.version.add_sliver_attribute(node, name, value)
                     except:
                         print >> sys.stderr, "FAILED: on node %s: %s=%s" % (node, name, value)
 
index a96c678..ef4a008 100755 (executable)
@@ -2,7 +2,8 @@
 
 import sys
 from sfa.client.sfi_commands import Commands
-from sfa.rspecs.rspec_parser import parse_rspec
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.version_manager import VersionManager
 
 command = Commands(usage="%prog [options] node1 node2...",
                    description="Add slivers to the RSpec. " +
@@ -24,17 +25,19 @@ if command.opts.outfile:
     outfile=file(command.opts.outfile,"w")
 else:
     outfile=sys.stdout
-
-rspec = parse_rspec(infile)
-rspec.type = 'request'
+request_rspec = RSpec(infile)
 nodes = file(command.opts.nodefile).read().split()
+version_manager = VersionManager()
 try:
-    if rspec.version['type'].lower() == 'protogeni':
-        rspec.xml.set('type', 'request')
+    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)
     slivers = [{'hostname': node} for node in nodes]
-    rspec.add_slivers(slivers)
+    manifest_rspec.version.merge(request_rspec)
+    manifest_rspec.version.add_slivers(slivers)
 except:
     print >> sys.stderr, "FAILED: %s" % nodes
     sys.exit(1)
-print >>outfile, rspec.toxml(cleanup=True)
+print >>outfile, manifest_rspec.toxml()
 sys.exit(0)
index f372488..53b2542 100755 (executable)
@@ -2,7 +2,7 @@
 
 import sys
 from sfa.client.sfi_commands import Commands
-from sfa.rspecs.rspec_parser import parse_rspec
+from sfa.rspecs.rspec import RSpec
 
 command = Commands(usage="%prog [options] [node1 node2...]",
                    description="Delete sliver attributes from the RSpec. " +
@@ -18,7 +18,7 @@ command.prep()
 
 if command.opts.infile:
     attrs = command.get_attribute_dict()
-    rspec = parse_rspec(command.opts.infile)
+    rspec = RSpec(command.opts.infile)
     nodes = []
     if command.opts.nodefile:
         f = open(command.opts.nodefile, "r")
@@ -31,13 +31,13 @@ if command.opts.infile:
         for value in attrs[name]:
             if not nodes:
                 try:
-                    rspec.remove_default_sliver_attribute(name, value)
+                    rspec.version.remove_default_sliver_attribute(name, value)
                 except:
                     print >> sys.stderr, "FAILED: on all nodes: %s=%s" % (name, value)
             else:
                 for node in nodes:
                     try:
-                        rspec.remove_sliver_attribute(node, name, value)
+                        rspec.version.remove_sliver_attribute(node, name, value)
                     except:
                         print >> sys.stderr, "FAILED: on node %s: %s=%s" % (node, name, value)
 
index 5b4f70b..be10f0b 100755 (executable)
@@ -2,7 +2,7 @@
 
 import sys
 from sfa.client.sfi_commands import Commands
-from sfa.rspecs.rspec_parser import parse_rspec
+from sfa.rspecs.rspec import RSpec
 
 command = Commands(usage="%prog [options] node1 node2...",
                    description="Delete slivers from the RSpec. " +
@@ -12,7 +12,7 @@ command.add_nodefile_option()
 command.prep()
 
 if command.opts.infile:
-    rspec = parse_rspec(command.opts.infile)
+    rspec = RSpec(command.opts.infile)
     nodes = []
     if command.opts.nodefile:
         f = open(command.opts.nodefile, "r")
@@ -21,11 +21,11 @@ if command.opts.infile:
        
     try:
         slivers = [{'hostname': node} for node in nodes]
-        rspec.remove_slivers(slivers)
+        rspec.version.remove_slivers(slivers)
+        print rspec.toxml()
     except:
         print >> sys.stderr, "FAILED: %s" % nodes 
 
-    print rspec.toxml()
     
 
     
index 305bf25..f9e794b 100755 (executable)
@@ -2,7 +2,7 @@
 
 import sys
 from sfa.client.sfi_commands import Commands
-from sfa.rspecs.rspec_parser import parse_rspec 
+from sfa.rspecs.rspec import RSpec 
 
 command = Commands(usage="%prog [options]",
                    description="List all nodes in the RSpec. " + 
@@ -11,8 +11,8 @@ command = Commands(usage="%prog [options]",
 command.prep()
 
 if command.opts.infile:
-    rspec = parse_rspec(command.opts.infile)
-    nodes = rspec.get_nodes()
+    rspec = RSpec(command.opts.infile)
+    nodes = rspec.version.get_nodes()
     if command.opts.outfile:
         sys.stdout = open(command.opts.outfile, 'w')
     
index 08173ec..cf76823 100755 (executable)
@@ -2,7 +2,7 @@
 
 import sys
 from sfa.client.sfi_commands import Commands
-from sfa.rspecs.rspec_parser import parse_rspec
+from sfa.rspecs.rspec import RSpec
 
 command = Commands(usage="%prog [options]",
                    description="List all slivers in the RSpec. " + 
@@ -12,11 +12,11 @@ command.add_show_attributes_option()
 command.prep()
 
 if command.opts.infile:
-    rspec = parse_rspec(command.opts.infile)
-    nodes = rspec.get_nodes_with_slivers()
+    rspec = RSpec(command.opts.infile)
+    nodes = rspec.version.get_nodes_with_slivers()
     
     if command.opts.showatt:
-        defaults = rspec.get_default_sliver_attributes()
+        defaults = rspec.version.get_default_sliver_attributes()
         if defaults:
             print "ALL NODES"
             for (name, value) in defaults:
@@ -25,7 +25,7 @@ if command.opts.infile:
     for node in nodes:
         print node
         if command.opts.showatt:
-            atts = rspec.get_sliver_attributes(node)
+            atts = rspec.version.get_sliver_attributes(node)
             for (name, value) in atts:
                 print "  %s: %s" % (name, value)
 
index 9a18046..ea8f2af 100644 (file)
@@ -1,7 +1,7 @@
 from __future__ import with_statement 
 
 import sys
-import os
+import os, errno
 import logging
 import datetime
 
@@ -630,6 +630,34 @@ def CreateSliver(api, slice_xrn, creds, xml, users, call_id):
     # with enough data for the client to understand what's happened
     return xml
 
+##
+# Return information on the IP addresses bound to each slice's instances
+#
+def dumpInstanceInfo():
+    logger = logging.getLogger('EucaMeta')
+    outdir = "/var/www/html/euca/"
+    outfile = outdir + "instances.txt"
+
+    try:
+        os.makedirs(outdir)
+    except OSError, e:
+        if e.errno != errno.EEXIST:
+            raise
+
+    dbResults = Meta.select(
+        AND(Meta.q.pri_addr != None,
+            Meta.q.state    == 'running')
+        )
+    dbResults = list(dbResults)
+    f = open(outfile, "w")
+    for r in dbResults:
+        instId = r.instance.instance_id
+        ipaddr = r.pri_addr
+        hrn = r.instance.slice.slice_hrn
+        logger.debug('[dumpInstanceInfo] %s %s %s' % (instId, ipaddr, hrn))
+        f.write("%s %s %s\n" % (instId, ipaddr, hrn))
+    f.close()
+
 ##
 # A separate process that will update the meta data.
 #
@@ -653,6 +681,8 @@ def updateMeta():
         logger.debug('[update process] dbResults: %s' % dbResults)
         instids = []
         for r in dbResults:
+            if not r.instance:
+                continue
             instids.append(r.instance.instance_id)
         logger.debug('[update process] Instance Id: %s' % ', '.join(instids))
 
@@ -678,6 +708,8 @@ def updateMeta():
             dbInst.meta.pub_addr = ipData['pub_addr']
             dbInst.meta.state    = 'running'
 
+        dumpInstanceInfo()
+
 def GetVersion(api):
     xrn=Xrn(api.hrn)
     request_rspec_versions = [dict(sfa_rspec_version)]
index f723f49..ad3069a 100644 (file)
@@ -1,5 +1,3 @@
-
-
 import datetime
 import time
 import traceback
@@ -24,23 +22,29 @@ from sfa.plc.api import SfaAPI
 from sfa.plc.aggregate import Aggregate
 from sfa.plc.slices import *
 from sfa.util.version import version_core
-from sfa.rspecs.rspec_version import RSpecVersion
-from sfa.rspecs.sfa_rspec import sfa_rspec_version
-from sfa.rspecs.pg_rspec import pg_rspec_ad_version, pg_rspec_request_version
-from sfa.rspecs.rspec_parser import parse_rspec 
+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
 
 def GetVersion(api):
+
+    version_manager = VersionManager()
+    ad_rspec_versions = []
+    request_rspec_versions = []
+    for rspec_version in version_manager.versions:
+        if rspec_version.content_type in ['*', 'ad']:
+            ad_rspec_versions.append(rspec_version.to_dict())
+        if rspec_version.content_type in ['*', 'request']:
+            request_rspec_versions.append(rspec_version.to_dict()) 
+    default_rspec_version = version_manager.get_version("sfa 1").to_dict()
     xrn=Xrn(api.hrn)
-    request_rspec_versions = [dict(pg_rspec_request_version), dict(sfa_rspec_version)]
-    ad_rspec_versions = [dict(pg_rspec_ad_version), dict(sfa_rspec_version)]
     version_more = {'interface':'aggregate',
                     'testbed':'myplc',
                     'hrn':xrn.get_hrn(),
                     'request_rspec_versions': request_rspec_versions,
                     'ad_rspec_versions': ad_rspec_versions,
-                    'default_ad_rspec': dict(sfa_rspec_version)
+                    'default_ad_rspec': default_rspec_version
                     }
     return version_core(version_more)
 
@@ -178,8 +182,8 @@ def CreateSliver(api, slice_xrn, creds, rspec_string, users, call_id):
         slice_record = users[0].get('slice_record', {})
 
     # parse rspec
-    rspec = parse_rspec(rspec_string)
-    requested_attributes = rspec.get_slice_attributes()
+    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)
@@ -191,7 +195,7 @@ def CreateSliver(api, slice_xrn, creds, rspec_string, users, call_id):
     slices.verify_slice_attributes(slice, requested_attributes)
     
     # add/remove slice from nodes
-    requested_slivers = [str(host) for host in rspec.get_nodes_with_slivers()]
+    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.
@@ -292,19 +296,20 @@ def ListSlices(api, creds, call_id):
 
     return slice_urns
     
-def ListResources(api, creds, options,call_id):
+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', '')
+    xrn = options.get('geni_slice_urn', None)
     (hrn, type) = urn_to_hrn(xrn)
 
+    version_manager = VersionManager()
     # get the rspec's return format from options
-    rspec_version = RSpecVersion(options.get('rspec_version'))
-    version_string = "rspec_%s" % (rspec_version.get_version_name())
+    rspec_version = version_manager.get_version(options.get('rspec_version'))
+    version_string = "rspec_%s" % (rspec_version.to_string())
 
     #panos adding the info option to the caching key (can be improved)
     if options.get('info'):
-       version_string = version_string + "_"+options.get('info', 'default')
+        version_string = version_string + "_"+options.get('info', 'default')
 
     # look in cache first
     if caching and api.cache and not xrn:
@@ -317,7 +322,6 @@ def ListResources(api, creds, options,call_id):
     #panos: passing user-defined options
     #print "manager options = ",options
     aggregate = Aggregate(api, options)
-
     rspec =  aggregate.get_rspec(slice_xrn=xrn, version=rspec_version)
 
     # cache the result
index 549fc0b..7ed75ed 100644 (file)
@@ -15,13 +15,10 @@ from sfa.util.rspec import *
 from sfa.util.specdict import *
 from sfa.util.faults import *
 from sfa.util.record import SfaRecord
-from sfa.rspecs.pg_rspec import PGRSpec
-from sfa.rspecs.sfa_rspec import SfaRSpec
 from sfa.rspecs.rspec_converter import RSpecConverter
-from sfa.rspecs.rspec_parser import parse_rspec    
-from sfa.rspecs.rspec_version import RSpecVersion
-from sfa.rspecs.sfa_rspec import sfa_rspec_version
-from sfa.rspecs.pg_rspec import pg_rspec_ad_version, pg_rspec_request_version   
+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 *
@@ -64,16 +61,23 @@ def GetVersion(api):
     # peers explicitly in aggregates.xml
     peers =dict ([ (peername,get_serverproxy_url(v)) for (peername,v) in api.aggregates.iteritems()
                    if peername != api.hrn])
-    xrn=Xrn (api.hrn)
-    request_rspec_versions = [dict(pg_rspec_request_version), dict(sfa_rspec_version)]
-    ad_rspec_versions = [dict(pg_rspec_ad_version), dict(sfa_rspec_version)]
+    version_manager = VersionManager()
+    ad_rspec_versions = []
+    request_rspec_versions = []
+    for rspec_version in version_manager.versions:
+        if rspec_version in ['*', 'ad']:
+            request_rspec_versions.append(rspec_version.to_dict())
+        if rspec_version in ['*', 'request']:
+            request_rspec_version.append(rspec_version.to_dict())
+    default_rspec_version = version_manager.get_version("sfa 1").to_dict()
+    xrn=Xrn(api.hrn)
     version_more = {'interface':'slicemgr',
                     'hrn' : xrn.get_hrn(),
                     'urn' : xrn.get_urn(),
                     'peers': peers,
                     'request_rspec_versions': request_rspec_versions,
                     'ad_rspec_versions': ad_rspec_versions,
-                    'default_ad_rspec': dict(sfa_rspec_version)
+                    'default_ad_rspec': default_rspec_version
                     }
     sm_version=version_core(version_more)
     # local aggregate if present needs to have localhost resolved
@@ -96,13 +100,14 @@ def add_slicemgr_stat(rspec, callname, aggname, elapsed, status):
         if stats_tags:
             stats_tag = stats_tags[0]
         else:
-            stats_tag = etree.SubElement(rspec.xml, "statistics", call=callname)
+            stats_tag = etree.SubElement(rspec.xml.root, "statistics", call=callname)
 
         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)))
 
 def ListResources(api, creds, options, call_id):
+    version_manager = VersionManager()
     def _ListResources(aggregate, server, credential, opts, call_id):
 
         my_opts = copy(opts)
@@ -114,11 +119,11 @@ def ListResources(api, creds, options, call_id):
             version = api.get_cached_server_version(server)
             # force ProtoGENI aggregates to give us a v2 RSpec
             if 'sfa' not in version.keys():
-                my_opts['rspec_version'] = pg_rspec_ad_version
+                my_opts['rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
             rspec = server.ListResources(*args)
             return {"aggregate": aggregate, "rspec": rspec, "elapsed": time.time()-tStart, "status": "success"}
         except Exception, e:
-            api.logger.warn("ListResources failed at %s: %s" %(server.url, str(e)))
+            api.logger.log_exc("ListResources failed at %s" %(server.url))
             return {"aggregate": aggregate, "elapsed": time.time()-tStart, "status": "exception"}
 
     if Callids().already_handled(call_id): return ""
@@ -130,8 +135,8 @@ def ListResources(api, creds, options, call_id):
         del(options['geni_compressed'])
 
     # get the rspec's return format from options
-    rspec_version = RSpecVersion(options.get('rspec_version'))
-    version_string = "rspec_%s" % (rspec_version.get_version_name())
+    rspec_version = version_manager.get_version(options.get('rspec_version'))
+    version_string = "rspec_%s" % (rspec_version.to_string())
 
     # look in cache first
     if caching and api.cache and not xrn:
@@ -160,17 +165,17 @@ def ListResources(api, creds, options, call_id):
         threads.run(_ListResources, aggregate, server, credentials, options, call_id)
 
     results = threads.get_results()
-    rspec_version = RSpecVersion(options.get('rspec_version'))
-    if rspec_version['type'] == pg_rspec_ad_version['type']:
-        rspec = PGRSpec()
-    else:
-        rspec = SfaRSpec()
-
+    rspec_version = version_manager.get_version(options.get('rspec_version'))
+    if xrn:    
+        result_version = version_manager._get_version(rspec_version.type, rspec_version.version, 'manifest')
+    else: 
+        result_version = version_manager._get_version(rspec_version.type, rspec_version.version, 'ad')
+    rspec = RSpec(version=result_version)
     for result in results:
         add_slicemgr_stat(rspec, "ListResources", result["aggregate"], result["elapsed"], result["status"])
         if result["status"]=="success":
             try:
-                rspec.merge(result["rspec"])
+                rspec.version.merge(result["rspec"])
             except:
                 api.logger.log_exc("SM.ListResources: Failed to merge aggregate rspec")
 
@@ -183,17 +188,23 @@ def ListResources(api, creds, options, call_id):
 
 def CreateSliver(api, xrn, creds, rspec_str, users, call_id):
 
+    version_manager = VersionManager()
     def _CreateSliver(aggregate, server, xrn, credential, rspec, users, call_id):
         tStart = time.time()
         try:
             # Need to call GetVersion at an aggregate to determine the supported
             # rspec type/format beofre calling CreateSliver at an Aggregate.
             server_version = api.get_cached_server_version(server)
+            requested_users = users
             if 'sfa' not in server_version and 'geni_api' in server_version:
                 # sfa aggregtes support both sfa and pg rspecs, no need to convert
                 # if aggregate supports sfa rspecs. otherwise convert to pg rspec
-                rspec = RSpecConverter.to_pg_rspec(rspec)
-            args = [xrn, credential, rspec, users]
+                rspec = RSpec(RSpecConverter.to_pg_rspec(rspec, 'request'))
+                filter = {'component_manager_id': server_version['urn']}
+                rspec.filter(filter)
+                rspec = rspec.toxml()
+                requested_users = sfa_to_pg_users_arg(users)
+            args = [xrn, credential, rspec, requested_users]
             if _call_id_supported(api, server):
                 args.append(call_id)
             rspec = server.CreateSliver(*args)
@@ -206,7 +217,7 @@ def CreateSliver(api, xrn, creds, rspec_str, users, call_id):
     # Validate the RSpec against PlanetLab's schema --disabled for now
     # The schema used here needs to aggregate the PL and VINI schemas
     # schema = "/var/www/html/schemas/pl.rng"
-    rspec = parse_rspec(rspec_str)
+    rspec = RSpec(rspec_str)
     schema = None
     if schema:
         rspec.validate(schema)
@@ -235,15 +246,16 @@ def CreateSliver(api, xrn, creds, rspec_str, users, call_id):
         threads.run(_CreateSliver, aggregate, server, xrn, credential, rspec.toxml(), users, call_id)
             
     results = threads.get_results()
-    rspec = SfaRSpec()
+    manifest_version = version_manager._get_version(rspec.version.type, rspec.version.version, 'manifest')
+    result_rspec = RSpec(version=manifest_version)
     for result in results:
-        add_slicemgr_stat(rspec, "CreateSliver", result["aggregate"], result["elapsed"], result["status"])
+        add_slicemgr_stat(result_rspec, "CreateSliver", result["aggregate"], result["elapsed"], result["status"])
         if result["status"]=="success":
             try:
-                rspec.merge(result["rspec"])
+                result_rspec.version.merge(result["rspec"])
             except:
                 api.logger.log_exc("SM.CreateSliver: Failed to merge aggregate rspec")
-    return rspec.toxml()
+    return result_rspec.toxml()
 
 def RenewSliver(api, xrn, creds, expiration_time, call_id):
     def _RenewSliver(server, xrn, creds, expiration_time, call_id):
index ef4705b..f39fd61 100644 (file)
@@ -1,9 +1,11 @@
 #!/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.rspecs.sfa_rspec import SfaRSpec
+#from sfa.rspecs.pg_rspec  import PGRSpec
+#from sfa.rspecs.rspec_version import RSpecVersion
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.version_manager import VersionManager
 
 class Aggregate:
 
@@ -77,19 +79,14 @@ class Aggregate:
 
     def get_rspec(self, slice_xrn=None, version = None):
         self.prepare()
-        rspec = None
-        rspec_version = RSpecVersion(version)
-        if slice_xrn:
-            type = 'manifest'
-        else:
-            type = 'advertisement' 
-        if rspec_version['type'].lower() == 'protogeni':
-            rspec = PGRSpec(type=type)
-        elif rspec_version['type'].lower() == 'sfa':
-            rspec = SfaRSpec(type=type, user_options=self.user_options)
+        version_manager = VersionManager()
+        version = version_manager.get_version(version)
+        if not slice_xrn:
+            rspec_version = version_manager._get_version(version.type, version.version, 'ad')
         else:
-            rspec = SfaRSpec(type=type, user_options=self.user_options)
-
+            rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
+               
+        rspec = RSpec(version=rspec_version, user_options=self.user_options)
         # get slice details if specified
         slice = None
         if slice_xrn:
@@ -102,7 +99,7 @@ class Aggregate:
         # filter out nodes with a whitelist:
         valid_nodes = [] 
         for node in self.nodes.values():
-            # only doing this becuase protogeni rspec needs
+            # only doing this because protogeni rspec needs
             # to advertise available initscripts 
             node['pl_initscripts'] = self.pl_initscripts
 
@@ -113,9 +110,9 @@ class Aggregate:
             elif not slice and not node['slice_ids_whitelist']:
                 valid_nodes.append(node)
     
-        rspec.add_nodes(valid_nodes)
-        rspec.add_interfaces(self.interfaces.values()) 
-        rspec.add_links(self.links.values())
+        rspec.version.add_nodes(valid_nodes)
+        rspec.version.add_interfaces(self.interfaces.values()) 
+        rspec.version.add_links(self.links.values())
 
         # add slivers
         if slice_xrn and slice:
@@ -127,7 +124,7 @@ class Aggregate:
                 # if tag isn't bound to a node then it applies to all slivers
                 # and belongs in the <sliver_defaults> tag
                 if not tag['node_id']:
-                    rspec.add_default_sliver_attribute(tag['tagname'], tag['value'], self.api.hrn)
+                    rspec.version.add_default_sliver_attribute(tag['tagname'], tag['value'], self.api.hrn)
 
             for node_id in slice['node_ids']:
                 try:
@@ -144,6 +141,6 @@ class Aggregate:
                             sliver['tags'].append(tag)
                 except:
                     self.api.logger.log_exc('unable to add sliver %s to node %s' % (slice['name'], node_id))
-            rspec.add_slivers(slivers, sliver_urn=slice_xrn)
+            rspec.version.add_slivers(slivers, sliver_urn=slice_xrn)
 
-        return rspec.toxml(cleanup=True)
+        return rspec.toxml()
diff --git a/sfa/rspecs/pg_rspec.py b/sfa/rspecs/pg_rspec.py
deleted file mode 100755 (executable)
index 405a820..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-#!/usr/bin/python
-from copy import deepcopy
-from lxml import etree
-from StringIO import StringIO
-from sfa.rspecs.rspec import RSpec 
-from sfa.util.xrn import *
-from sfa.util.plxrn import hostname_to_urn, xrn_to_hostname
-from sfa.util.config import Config 
-from sfa.rspecs.rspec_version import RSpecVersion 
-
-_ad_version = {'type':  'ProtoGENI',
-            'version': '2',
-            'schema': 'http://www.protogeni.net/resources/rspec/2/ad.xsd',
-            'namespace': 'http://www.protogeni.net/resources/rspec/2',
-            'extensions':  [
-                'http://www.protogeni.net/resources/rspec/ext/gre-tunnel/1',
-                'http://www.protogeni.net/resources/rspec/ext/other-ext/3'
-            ]
-}
-
-_request_version = {'type':  'ProtoGENI',
-            'version': '2',
-            'schema': 'http://www.protogeni.net/resources/rspec/2/request.xsd',
-            'namespace': 'http://www.protogeni.net/resources/rspec/2',
-            'extensions':  [
-                'http://www.protogeni.net/resources/rspec/ext/gre-tunnel/1',
-                'http://www.protogeni.net/resources/rspec/ext/other-ext/3'
-            ]
-}
-pg_rspec_ad_version = RSpecVersion(_ad_version)
-pg_rspec_request_version = RSpecVersion(_request_version)
-
-class PGRSpec(RSpec):
-    xml = None
-    header = '<?xml version="1.0"?>\n'
-    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.protogeni.net/resources/rspec/2" xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2 http://www.protogeni.net/resources/rspec/2/%(rspec_type)s.xsd" xmlns:flack="http://www.protogeni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'
-
-    def __init__(self, rspec="", namespaces={}, type=None):
-        if not type:
-            type = 'advertisement'
-        self.type = type
-
-        if type == 'advertisement':
-            self.version = pg_rspec_ad_version
-            rspec_type = 'ad'
-        else:
-            self.version = pg_rspec_request_version
-            rspec_type = type
-        
-        self.template = self.template % {'rspec_type': rspec_type}
-
-        if not namespaces:
-            self.namespaces = {'rspecv2': self.version['namespace'],
-                               'flack': 'http://www.protogeni.net/resources/rspec/ext/flack/1',
-                               'planetlab': 'http://www.planet-lab.org/resources/sfa/ext/planetlab/1' }
-
-        else:
-            self.namespaces = namespaces 
-
-        if rspec:
-            self.parse_rspec(rspec, self.namespaces)
-        else: 
-            self.create()
-
-    def create(self):
-        RSpec.create(self)
-        if self.type:
-            self.xml.set('type', self.type) 
-       
-    def get_network(self):
-        network = None 
-        nodes = self.xml.xpath('//rspecv2:node[@component_manager_id][1]', namespaces=self.namespaces)
-        if nodes:
-            network  = nodes[0].get('component_manager_id')
-        return network
-
-    def get_networks(self):
-        networks = self.xml.xpath('//rspecv2:node[@component_manager_id]/@component_manager_id', namespaces=self.namespaces)
-        return set(networks)
-
-    def get_node_element(self, hostname, network=None):
-        nodes = self.xml.xpath('//rspecv2:node[@component_id[contains(., "%s")]] | node[@component_id[contains(., "%s")]]' % (hostname, hostname), namespaces=self.namespaces)
-        if isinstance(nodes,list) and nodes:
-            return nodes[0]
-        else:
-            return None
-
-    def get_node_elements(self, network=None):
-        nodes = self.xml.xpath('//rspecv2:node | //node', namespaces=self.namespaces)
-        return nodes
-
-    def get_nodes(self, network=None):
-        xpath = '//rspecv2:node[@component_name]/@component_id | //node[@component_name]/@component_id'
-        nodes = self.xml.xpath(xpath, namespaces=self.namespaces)
-        nodes = [xrn_to_hostname(node) for node in nodes]
-        return nodes 
-
-    def get_nodes_with_slivers(self, network=None):
-        if network:
-            nodes = self.xml.xpath('//rspecv2:node[@component_manager_id="%s"][sliver_type]/@component_id' % network, namespaces=self.namespaces)
-        else:
-            nodes = self.xml.xpath('//rspecv2:node[rspecv2:sliver_type]/@component_id', namespaces=self.namespaces)
-        nodes = [xrn_to_hostname(node) for node in nodes]
-        return nodes
-
-    def get_nodes_without_slivers(self, network=None):
-        return []
-
-    def get_sliver_attributes(self, hostname, network=None):
-        node = self.get_node_element(hostname, network)
-        sliver = node.xpath('./rspecv2:sliver_type', namespaces=self.namespaces)
-        if sliver is not None and isinstance(sliver, list):
-            sliver = sliver[0]
-        return self.attributes_list(sliver)
-   
-    def get_slice_attributes(self, network=None):
-        slice_attributes = []
-        nodes_with_slivers = self.get_nodes_with_slivers(network)
-        # TODO: default sliver attributes in the PG rspec?
-        default_ns_prefix = self.namespaces['rspecv2']
-        for node in nodes_with_slivers:
-            sliver_attributes = self.get_sliver_attributes(node, network)
-            for sliver_attribute in sliver_attributes:
-                name=str(sliver_attribute[0]) 
-                text =str(sliver_attribute[1])
-                attribs = sliver_attribute[2]
-                # we currently only suppor the <initscript> and <flack> attributes 
-                if  'info' in name:
-                    attribute = {'name': 'flack_info', 'value': str(attribs), 'node_id': node}
-                    slice_attributes.append(attribute) 
-                elif 'initscript' in name: 
-                    if attribs is not None and 'name' in attribs:
-                        value = attribs['name']
-                    else:
-                        value = text
-                    attribute = {'name': 'initscript', 'value': value, 'node_id': node}
-                    slice_attributes.append(attribute) 
-
-        return slice_attributes
-
-    def attributes_list(self, elem):
-        opts = []
-        if elem is not None:
-            for e in elem:
-                opts.append((e.tag, str(e.text).strip(), e.attrib))
-        return opts
-
-    def get_default_sliver_attributes(self, network=None):
-        return []
-
-    def add_default_sliver_attribute(self, name, value, network=None):
-        pass
-
-    def add_nodes(self, nodes, check_for_dupes=False):
-        if not isinstance(nodes, list):
-            nodes = [nodes]
-        for node in nodes:
-            urn = ""
-            if check_for_dupes and \
-              self.xml.xpath('//rspecv2:node[@component_uuid="%s"]' % urn, namespaces=self.namespaces):
-                # node already exists
-                continue
-                
-            node_tag = etree.SubElement(self.xml, 'node', exclusive='false')
-            if 'network_urn' in node:
-                node_tag.set('component_manager_id', node['network_urn'])
-            if 'urn' in node:
-                node_tag.set('component_id', node['urn'])
-            if 'hostname' in node:
-                node_tag.set('component_name', node['hostname'])
-            # TODO: should replace plab-pc with pc model 
-            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')
-            
-            pl_initscripts = node.get('pl_initscripts', {})
-            for pl_initscript in pl_initscripts.values():
-                etree.SubElement(sliver_type_tag, '{%s}initscript' % self.namespaces['planetlab'], name=pl_initscript['name'])
-
-            # protogeni uses the <sliver_type> tag to identify the types of
-            # vms available at the node. 
-            # only add location tag if longitude and latitude are not null
-            if 'site' in node:
-                longitude = node['site'].get('longitude', None)
-                latitude = node['site'].get('latitude', None)
-                if longitude and latitude:
-                    location_tag = etree.SubElement(node_tag, 'location', country="us", \
-                                                    longitude=str(longitude), latitude=str(latitude))
-
-    def merge_node(self, source_node_tag):
-        # this is untested
-        self.xml.append(deepcopy(source_node_tag))
-
-    def add_slivers(self, slivers, sliver_urn=None, no_dupes=False): 
-
-        # all nodes hould already be present in the rspec. Remove all
-        # nodes that done have slivers
-        slivers = self._process_slivers(slivers)
-        slivers_dict = {}
-        for sliver in slivers:
-            slivers_dict[sliver['hostname']] = sliver
-    
-        nodes = self.get_node_elements()
-        for node in nodes:
-            urn = node.get('component_id')
-            hostname = xrn_to_hostname(urn)
-            if hostname not in slivers_dict:
-                parent = node.getparent()
-                parent.remove(node)
-            else:
-                sliver_info = slivers_dict[hostname]
-                node.set('client_id', hostname)
-                if sliver_urn:
-                    slice_id = sliver_info.get('slice_id', -1)
-                    node_id = sliver_info.get('node_id', -1)
-                    sliver_id = urn_to_sliver_id(sliver_urn, slice_id, node_id)
-                    node.set('sliver_id', sliver_id)
-
-                # remove existing sliver_type tags,it needs to be recreated
-                sliver_elem = node.xpath('./rspecv2:sliver_type | ./sliver_type', namespaces=self.namespaces)
-                if sliver_elem and isinstance(sliver_elem, list):
-                    sliver_elem = sliver_elem[0]
-                    node.remove(sliver_elem)
-                    
-                sliver_elem = etree.SubElement(node, 'sliver_type', name='plab-vnode')
-                for tag in sliver_info['tags']:
-                    if tag['tagname'] == 'flack_info':
-                        e = etree.SubElement(sliver_elem, '{%s}info' % self.namespaces['flack'], attrib=eval(tag['value']))
-                    elif tag['tagname'] == 'initscript':
-                        e = etree.SubElement(sliver_elem, '{%s}initscript' % self.namespaces['planetlab'], attrib={'name': tag['value']})
-                            
-                              
-     
-    def add_default_sliver_attribute(self, name, value, network=None):
-        pass
-
-    def add_interfaces(self, interfaces, no_dupes=False):
-        pass
-
-    def add_links(self, links, no_dupes=False):
-        pass
-
-
-    def merge(self, in_rspec):
-        """
-        Merge contents for specified rspec with current rspec
-        """
-        
-        # just copy over all the child elements under the root element
-        tree = etree.parse(StringIO(in_rspec))
-        root = tree.getroot()
-        for child in root.getchildren():
-            self.xml.append(child)
-                  
-    def cleanup(self):
-        # remove unncecessary elements, attributes
-        if self.type in ['request', 'manifest']:
-            # remove 'available' element from remaining node elements
-            self.remove_element('//rspecv2:available | //available')
-
-if __name__ == '__main__':
-    rspec = PGRSpec()
-    rspec.add_nodes([1])
-    print rspec
index 4686e4c..42e7ccd 100755 (executable)
@@ -2,8 +2,8 @@
 from lxml import etree
 from StringIO import StringIO
 from sfa.util.xrn import *
-from sfa.rspecs.pg_rspec import PGRSpec 
-from sfa.rspecs.sfa_rspec import SfaRSpec
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.version_manager import VersionManager
 
 xslt='''<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" indent="no"/>
@@ -34,36 +34,39 @@ transform=etree.XSLT(xslt_doc)
 class PGRSpecConverter:
 
     @staticmethod
-    def to_sfa_rspec(rspec):
-        if isinstance(rspec, PGRSpec):
+    def to_sfa_rspec(rspec, content_type = None):
+        if not isinstance(rspec, RSpec):
+            pg_rspec = RSpec(rspec)
+        else:
             pg_rspec = rspec
-        else:        
-            pg_rspec = PGRSpec(rspec=rspec)
-        sfa_rspec = SfaRSpec()
+        
+        version_manager = VersionManager()
+        sfa_version = version_manager._get_version('sfa', '1')    
+        sfa_rspec = RSpec(version=sfa_version)
 
         # get network
-        network_urn = pg_rspec.get_network()
+        network_urn = pg_rspec.version.get_network()
         network,  _ = urn_to_hrn(network_urn)
-        network_element = sfa_rspec.add_element('network', {'name': network, 'id': network})
+        network_element = sfa_rspec.xml.add_element('network', {'name': network, 'id': network})
         
         # get nodes
-        pg_nodes_elements = pg_rspec.get_node_elements()
-        nodes_with_slivers = pg_rspec.get_nodes_with_slivers()
+        pg_nodes_elements = pg_rspec.version.get_node_elements()
+        nodes_with_slivers = pg_rspec.version.get_nodes_with_slivers()
         i = 1
         for pg_node_element in pg_nodes_elements:
             attribs = dict(pg_node_element.attrib.items()) 
             attribs['id'] = 'n'+str(i)
             
-            node_element = sfa_rspec.add_element('node', attribs, parent=network_element)
+            node_element = sfa_rspec.xml.add_element('node', attribs, parent=network_element)
             urn = pg_node_element.xpath('@component_id', namespaces=pg_rspec.namespaces)
             if urn:
                 urn = urn[0]
                 hostname = Xrn.urn_split(urn)[-1]
-                hostname_element = sfa_rspec.add_element('hostname', parent=node_element, text=hostname)
+                hostname_element = sfa_rspec.xml.add_element('hostname', parent=node_element, text=hostname)
                 if hostname in nodes_with_slivers:
-                    sfa_rspec.add_element('sliver', parent=node_element)
+                    sfa_rspec.xml.add_element('sliver', parent=node_element)
                      
-            urn_element = sfa_rspec.add_element('urn', parent=node_element, text=urn)
+            urn_element = sfa_rspec.xml.add_element('urn', parent=node_element, text=urn)
 
 
             # just copy over remaining child elements  
index 5872e33..73185fe 100755 (executable)
@@ -1,96 +1,56 @@
 #!/usr/bin/python 
-from lxml import etree
-from StringIO import StringIO
 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.rspecs.rspec_elements import RSpecElement, RSpecElements 
 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 RSpec:
-    header = '<?xml version="1.0"?>\n'
-    template = """<RSpec></RSpec>"""
-    xml = None
-    type = None
-    version = None
-    namespaces = None
-    user_options = {}
  
-    def __init__(self, rspec="", namespaces={}, type=None, user_options={}):
-        self.type = type
+    def __init__(self, rspec="", version=None, user_options={}):
+        self.header = '<?xml version="1.0"?>\n'
+        self.template = """<RSpec></RSpec>"""
+        self.version = None
+        self.xml = XML()
+        self.version_manager = VersionManager()
         self.user_options = user_options
         self.elements = {}
         if rspec:
-            self.parse_rspec(rspec, namespaces)
+            self.parse_xml(rspec)
         else:
-            self.create()
+            self.create(version)
 
-    def create(self):
+    def create(self, version=None):
         """
         Create root element
         """
+        self.version = self.version_manager.get_version(version)
+        self.namespaces = self.version.namespaces
+        self.parse_xml(self.version.template) 
         # eg. 2011-03-23T19:53:28Z 
         date_format = '%Y-%m-%dT%H:%M:%SZ'
         now = datetime.utcnow()
         generated_ts = now.strftime(date_format)
         expires_ts = (now + timedelta(hours=1)).strftime(date_format) 
-        self.parse_rspec(self.template, self.namespaces)
         self.xml.set('expires', expires_ts)
         self.xml.set('generated', generated_ts)
-    
-    def parse_rspec(self, rspec, namespaces={}):
-        """
-        parse rspec into etree
-        """
-        parser = etree.XMLParser(remove_blank_text=True)
-        try:
-            tree = etree.parse(rspec, parser)
-        except IOError:
-            # 'rspec' file doesnt exist. 'rspec' is proably an xml string
-            try:
-                tree = etree.parse(StringIO(rspec), parser)
-            except Exception, e:
-                raise InvalidRSpec(str(e))
-        self.xml = tree.getroot()  
-        if namespaces:
-           self.namespaces = namespaces
-
-    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 parse_xml(self, xml):
+        self.xml.parse_xml(xml)
+        self.version = None
+        if self.xml.schema:
+            self.version = self.version_manager.get_version_by_schema(self.xml.schema)
+        else:
+            #raise InvalidRSpec('unknown rspec schema: %s' % schema)
+            # TODO: Should start raising an exception once SFA defines a schema.
+            # for now we just use the default  
+            self.version = self.version_manager.get_version()
+        self.version.xml = self.xml    
+        self.namespaces = self.xml.namespaces
+    
     def load_rspec_elements(self, rspec_elements):
         self.elements = {}
         for rspec_element in rspec_elements:
@@ -108,86 +68,6 @@ class RSpec:
             raise InvalidRSpecElement(element_type, extra=msg)
         return self.elements[element_type]
 
-    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 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]
@@ -206,49 +86,33 @@ class RSpec:
         return self.xpath(xpath)
 
     def merge(self, in_rspec):
-        pass
-
-    def cleanup(self):
-        """
-        Optional method which inheriting classes can choose to implent. 
-        """
-        pass 
-
-    def _process_slivers(self, slivers):
-        """
-        Creates a dict of sliver details for each sliver host
+        self.version.merge(in_rspec)
+
+    def filter(self, filter):
+        if 'component_manager_id' in filter:    
+            nodes = self.version.get_node_elements()
+            for node in nodes:
+                if 'component_manager_id' not in node.attrib or \
+                  node.attrib['component_manager_id'] != filter['component_manager_id']:
+                    parent = node.getparent()
+                    parent.remove(node) 
         
-        @param slivers a single hostname, list of hostanmes or list of dicts keys on hostname,
-        Returns a list of dicts 
-        """
-        if not isinstance(slivers, list):
-            slivers = [slivers]
-        dicts = []
-        for sliver in slivers:
-            if isinstance(sliver, dict):
-                dicts.append(sliver)
-            elif isinstance(sliver, basestring):
-                dicts.append({'hostname': sliver}) 
-        return dicts
 
-    def __str__(self):
-        return self.toxml()
+    def toxml(self, header=True):
+        if header:
+            return self.header + self.xml.toxml()
+        else:
+            return self.xml.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()
+        return self.xml.save(filename)
+         
 if __name__ == '__main__':
     rspec = RSpec('/tmp/resources.rspec')
     print rspec
-    #rspec.register_rspec_element(RSpecElements.NETWORK, 'network', '//network')
-    #rspec.register_rspec_element(RSpecElements.NODE, 'node', '//node')
-    #print rspec.find(RSpecElements.NODE)[0]
-    #print rspec.find(RSpecElements.NODE, depth=1)[0]
+    rspec.register_rspec_element(RSpecElements.NETWORK, 'network', '//network')
+    rspec.register_rspec_element(RSpecElements.NODE, 'node', '//node')
+    print rspec.get(RSpecElements.NODE)[0]
+    print rspec.get(RSpecElements.NODE, depth=1)[0]
 
index 6f96d4f..7dff2f0 100755 (executable)
@@ -2,30 +2,35 @@
 
 from sfa.rspecs.pg_rspec_converter import PGRSpecConverter
 from sfa.rspecs.sfa_rspec_converter import SfaRSpecConverter
-from sfa.rspecs.sfa_rspec import sfa_rspec_version
-from sfa.rspecs.pg_rspec import pg_rspec_ad_version, pg_rspec_request_version
-from sfa.rspecs.rspec_parser import parse_rspec
-
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.version_manager import VersionManager
 
 class RSpecConverter:
 
     @staticmethod
-    def to_sfa_rspec(in_rspec):
-        rspec = parse_rspec(in_rspec)
-        if rspec.version['type'] == sfa_rspec_version['type']: 
+    def to_sfa_rspec(in_rspec, content_type=None):
+        rspec = RSpec(in_rspec)
+        version_manager = VersionManager()
+        sfa_version = version_manager._get_version('sfa', '1')
+        pg_version = version_manager._get_version('protogeni', '2')
+        if rspec.version.type.lower() == sfa_version.type.lower(): 
           return in_rspec
-        elif rspec.version['type'] == pg_rspec_ad_version['type']:
-            return PGRSpecConverter.to_sfa_rspec(in_rspec)
+        elif rspec.version.type.lower() == pg_version.type.lower(): 
+            return PGRSpecConverter.to_sfa_rspec(in_rspec, content_type)
         else:
             return in_rspec 
 
     @staticmethod 
-    def to_pg_rspec(in_rspec):
-        rspec = parse_rspec(in_rspec)
-        if rspec.version['type'] == pg_rspec_ad_version['type']:
+    def to_pg_rspec(in_rspec, content_type=None):
+        rspec = RSpec(in_rspec)
+        version_manager = VersionManager()
+        sfa_version = version_manager._get_version('sfa', '1')
+        pg_version = version_manager._get_version('protogeni', '2')
+
+        if rspec.version.type.lower() == pg_version.type.lower(): 
             return in_rspec
-        elif rspec.version['type'] == sfa_rspec_version['type']:
-            return SfaRSpecConverter.to_pg_rspec(in_rspec)
+        elif rspec.version.type.lower() == sfa_version.type.lower(): 
+            return SfaRSpecConverter.to_pg_rspec(in_rspec, content_type)
         else:
             return in_rspec 
 
diff --git a/sfa/rspecs/rspec_parser.py b/sfa/rspecs/rspec_parser.py
deleted file mode 100755 (executable)
index b29329b..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/python
-from sfa.rspecs.sfa_rspec import SfaRSpec
-from sfa.rspecs.pg_rspec import PGRSpec
-from sfa.rspecs.rspec import RSpec
-from lxml import etree 
-
-def parse_rspec(in_rspec):
-    rspec = RSpec(rspec=in_rspec)
-    # really simple check
-    # TODO: check against schema instead
-    out_rspec = None 
-    if rspec.xml.xpath('//network'):
-        #out_rspec = SfaRSpec(in_rspec)
-        out_rspec = SfaRSpec()
-        out_rspec.type = 'SFA'
-        out_rspec.xml = rspec.xml
-    else:
-        #out_rspec = PGRSpec(in_rspec)
-        # TODO: determine if this is an ad or request
-        out_rspec = PGRSpec()
-        out_rspec.type = 'ProtoGENI'
-        out_rspec.xml = rspec.xml
-    return out_rspec
-
-if __name__ == '__main__':
-    
-    print "Parsing SFA RSpec:", 
-    rspec = parse_rspec('nodes.rspec')
-    print rspec.version
-    rspec = parse_rspec('protogeni.rspec')
-    print "Parsing ProtoGENI RSpec:", 
-    print rspec.version
-    
-    
-
old mode 100755 (executable)
new mode 100644 (file)
index e6cdcec..a670ddc
@@ -1,6 +1,33 @@
 #!/usr/bin/python
 from sfa.util.sfalogging import logger
 
+class BaseVersion:
+    type = None
+    content_type = None
+    version = None
+    schema = None
+    namespace = None
+    extensions = {}
+    namespaces = dict(extensions.items() + [('default', namespace)])
+    elements = []
+    enabled = False
+
+    def __init__(self, xml=None):
+        self.xml = xml
+
+    def to_dict(self):
+        return {
+            'type': self.type,
+            'version': self.version,
+            'schema': self.schema,
+            'namespace': self.namespace,
+            'extensions': self.extensions
+        }
+
+    def to_string(self):
+        return "%s %s" % (self.type, self.version)
+    
+
 class RSpecVersion(dict):
 
     fields = {'type': None,
diff --git a/sfa/rspecs/sfa_rspec.py b/sfa/rspecs/sfa_rspec.py
deleted file mode 100755 (executable)
index 56e302b..0000000
+++ /dev/null
@@ -1,380 +0,0 @@
-#!/usr/bin/python
-from copy import deepcopy
-from lxml import etree
-from StringIO import StringIO
-from sfa.rspecs.rspec import RSpec 
-from sfa.util.xrn import *
-from sfa.util.plxrn import hostname_to_urn
-from sfa.util.config import Config
-from sfa.rspecs.rspec_version import RSpecVersion  
-
-
-_version = { 'type': 'SFA', 
-             'version': '1' 
-}
-
-sfa_rspec_version = RSpecVersion(_version)
-
-class SfaRSpec(RSpec):
-    xml = None
-    header = '<?xml version="1.0"?>\n'
-    version = sfa_rspec_version
-
-    def create(self):
-        RSpec.create(self)
-        self.xml.set('type', 'SFA')
-
-    ###################
-    # Parser
-    ###################
-    def get_network_elements(self):
-        return self.xml.xpath('//network')
-
-    def get_networks(self):
-        return self.xml.xpath('//network[@name]/@name')
-
-    def get_node_element(self, hostname, network=None):
-        if network:
-            names = self.xml.xpath('//network[@name="%s"]//node/hostname' % network)
-        else:
-            names = self.xml.xpath('//node/hostname')
-        for name in names:
-            if str(name.text).strip() == hostname:
-                return name.getparent()
-        return None
-    def get_node_elements(self, network=None):
-        if network:
-            return self.xml.xpath('//network[@name="%s"]//node' % network)
-        else:
-            return self.xml.xpath('//node')
-
-    def get_nodes(self, network=None):
-        if network == None:
-            nodes = self.xml.xpath('//node/hostname/text()')
-        else:
-            nodes = self.xml.xpath('//network[@name="%s"]//node/hostname/text()' % network)
-
-        nodes = [node.strip() for node in nodes]
-        return nodes
-
-    def get_nodes_with_slivers(self, network = None):
-        if network:
-            nodes =  self.xml.xpath('//network[@name="%s"]//node[sliver]/hostname/text()' % network)   
-        else:
-            nodes = self.xml.xpath('//node[sliver]/hostname/text()')
-
-        nodes = [node.strip() for node in nodes]
-        return nodes     
-
-    def get_nodes_without_slivers(self, network=None): 
-        xpath_nodes_without_slivers = '//node[not(sliver)]/hostname/text()'
-        xpath_nodes_without_slivers_in_network = '//network[@name="%s"]//node[not(sliver)]/hostname/text()' 
-        if network:
-            return self.xml.xpath('//network[@name="%s"]//node[not(sliver)]/hostname/text()' % network)
-        else:
-            return self.xml.xpath('//node[not(sliver)]/hostname/text()')      
-
-
-    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_default_sliver_attributes(self, network=None):
-        if network:
-            defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)        
-        else:
-            defaults = self.xml.xpath("//sliver_defaults")
-        if isinstance(defaults, list) and defaults:
-            defaults = defaults[0]
-        return self.attributes_list(defaults)
-
-    def get_sliver_attributes(self, hostname, network=None):
-        attributes = [] 
-        node = self.get_node_element(hostname, network)
-        #sliver = node.find("sliver")
-        slivers = node.xpath('./sliver')
-        if isinstance(slivers, list) and slivers:
-            attributes = self.attributes_list(slivers[0])
-        return attributes
-
-    def get_slice_attributes(self, network=None):
-        slice_attributes = []
-        nodes_with_slivers = self.get_nodes_with_slivers(network)
-        for default_attribute in self.get_default_sliver_attributes(network):
-            attribute = {'name': str(default_attribute[0]), 'value': str(default_attribute[1]), 'node_id': None}
-            slice_attributes.append(attribute)
-        for node in nodes_with_slivers:
-            sliver_attributes = self.get_sliver_attributes(node, network)
-            for sliver_attribute in sliver_attributes:
-                attribute = {'name': str(sliver_attribute[0]), 'value': str(sliver_attribute[1]), 'node_id': node}
-                slice_attributes.append(attribute)    
-        return slice_attributes
-
-    def get_site_nodes(self, siteid, network=None):
-        if network:
-            nodes = self.xml.xpath('//network[@name="%s"]/site[@id="%s"]/node/hostname/text()' % \
-                                    (network, siteid))
-        else:
-            nodes = self.xml.xpath('//site[@id="%s"]/node/hostname/text()' % siteid)
-        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
-
-    def get_link(self, fromnode, tonode, network=None):
-        fromsite = fromnode.getparent()
-        tosite = tonode.getparent()
-        fromid = fromsite.get("id")
-        toid = tosite.get("id")
-        if network:
-            query = "//network[@name='%s']" % network + "/link[@endpoints = '%s %s']"
-        else:
-            query = "//link[@endpoints = '%s %s']"
-
-        results = self.rspec.xpath(query % (fromid, toid))
-        if not results:
-            results = self.rspec.xpath(query % (toid, fromid))
-        return results
-
-    def query_links(self, fromnode, tonode, network=None):
-        return get_link(fromnode, tonode, network)
-
-    def get_vlinks(self, network=None):
-        vlinklist = []
-        if network: 
-            vlinks = self.xml.xpath("//network[@name='%s']//vlink" % network)
-        else:
-            vlinks = self.xml.xpath("//vlink") 
-        for vlink in vlinks:
-            endpoints = vlink.get("endpoints")
-            (end1, end2) = endpoints.split()
-            if network: 
-                node1 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
-                                       (network, end1))[0]
-                node2 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
-                                       (network, end2))[0]
-            else: 
-                node1 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end1)[0]
-                node2 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end2)[0]
-            desc = "%s <--> %s" % (node1, node2)
-            kbps = vlink.find("kbps")
-            vlinklist.append((endpoints, desc, kbps.text))
-        return vlinklist
-
-    def get_vlink(self, endponts, network=None):
-        if network:
-            query = "//network[@name='%s']//vlink[@endpoints = '%s']" % (network, endpoints)
-        else:
-            query = "//vlink[@endpoints = '%s']" % (network, endpoints)
-        results = self.rspec.xpath(query)
-        return results
-        
-    def query_vlinks(self, endpoints, network=None):
-        return get_vlink(endpoints,network)
-
-    ##################
-    # Builder
-    ##################
-
-    def add_network(self, network):
-        network_tags = self.xml.xpath('//network[@name="%s"]' % network)
-        if not network_tags:
-            network_tag = etree.SubElement(self.xml, 'network', name=network)
-        else:
-            network_tag = network_tags[0]
-        return network_tag     
-
-    def add_nodes(self, nodes, network = None, no_dupes=False):
-        if not isinstance(nodes, list):
-            nodes = [nodes]
-        for node in nodes:
-            if no_dupes and \
-              self.get_node_element(node['hostname']):
-                # node already exists
-                continue
-
-            network_tag = self.xml
-            if 'network' in node:
-                network = node['network']
-                network_tag = self.add_network(network)
-     
-            node_tag = etree.SubElement(network_tag, 'node')
-            if 'network' in node:
-                node_tag.set('component_manager_id', hrn_to_urn(network, 'authority+sa'))
-            if 'urn' in node:
-                node_tag.set('component_id', node['urn']) 
-            if 'site_urn' in node:
-                node_tag.set('site_id', node['site_urn'])
-            if 'node_id' in node: 
-                node_tag.set('node_id', 'n'+str(node['node_id']))
-            if 'boot_state' in node:
-                node_tag.set('boot_state', node['boot_state']) 
-            if 'hostname' in node:
-                hostname_tag = etree.SubElement(node_tag, 'hostname').text = node['hostname']
-            if 'interfaces' in node:
-                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)
-            if 'tags' in node:
-                for tag in node['tags']:
-                   # expose this hard wired list of tags, plus the ones that are marked 'sfa' in their category 
-                   if tag['tagname'] in ['fcdistro', 'arch'] or 'sfa' in tag['category'].split('/'):
-                        tag_element = etree.SubElement(node_tag, tag['tagname']).text=tag['value']
-
-            if 'site' in node:
-                longitude = str(node['site']['longitude'])
-                latitude = str(node['site']['latitude'])
-                location = etree.SubElement(node_tag, 'location', country='unknown', \
-                                            longitude=longitude, latitude=latitude)
-
-    def merge_node(self, source_node_tag, network, no_dupes=False):
-        if no_dupes and self.get_node_element(node['hostname']):
-            # node already exists
-            return
-
-        network_tag = self.add_network(network)
-        network_tag.append(deepcopy(source_node_tag))
-
-    def add_interfaces(self, interfaces):
-        pass
-
-    def add_links(self, links):
-        pass
-
-    def add_slivers(self, slivers, network=None, sliver_urn=None, no_dupes=False):
-        # add slice name to network tag
-        network_tags = self.xml.xpath('//network')
-        if network_tags:
-            network_tag = network_tags[0]
-            network_tag.set('slice', urn_to_hrn(sliver_urn)[0])
-        slivers = self._process_slivers(slivers)
-        nodes_with_slivers = self.get_nodes_with_slivers(network)
-        for sliver in slivers:
-            if sliver['hostname'] in nodes_with_slivers:
-                continue
-            node_elem = self.get_node_element(sliver['hostname'], network)
-            sliver_elem = etree.SubElement(node_elem, 'sliver')
-            if 'tags' in sliver:
-                for tag in sliver['tags']:
-                    etree.SubElement(sliver_elem, tag['tagname']).text = value=tag['value']
-
-    def remove_slivers(self, slivers, network=None, no_dupes=False):
-        slivers = self._process_slivers(slivers)
-        for sliver in slivers:
-            node_elem = self.get_node_element(sliver['hostname'], network)
-            sliver_elem = node_elem.find('sliver')
-            if sliver_elem != None:
-                node_elem.remove(sliver_elem)
-    
-    def add_default_sliver_attribute(self, name, value, network=None):
-        if network:
-            defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
-        else:
-            defaults = self.xml.xpath("//sliver_defaults" % network)
-        if not defaults :
-            network_tag = self.xml.xpath("//network[@name='%s']" % network)
-            if isinstance(network_tag, list):
-                network_tag = network_tag[0]
-            defaults = self.add_element('sliver_defaults', attrs={}, parent=network_tag)
-        elif isinstance(defaults, list):
-            defaults = defaults[0]
-        self.add_attribute(defaults, name, value)
-
-    def add_sliver_attribute(self, hostname, name, value, network=None):
-        node = self.get_node_element(hostname, network)
-        sliver = node.find("sliver")
-        self.add_attribute(sliver, name, value)
-
-    def remove_default_sliver_attribute(self, name, value, network=None):
-        if network:
-            defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
-        else:
-            defaults = self.xml.xpath("//sliver_defaults" % network)
-        self.remove_attribute(defaults, name, value)
-
-    def remove_sliver_attribute(self, hostname, name, value, network=None):
-        node = self.get_node_element(hostname, network)
-        sliver = node.find("sliver")
-        self.remove_attribute(sliver, name, value)
-
-    def add_vlink(self, fromhost, tohost, kbps, network=None):
-        fromnode = self.get_node_element(fromhost, network)
-        tonode = self.get_node_element(tohost, network)
-        links = self.get_link(fromnode, tonode, network)
-
-        for link in links:
-            vlink = etree.SubElement(link, "vlink")
-            fromid = fromnode.get("id")
-            toid = tonode.get("id")
-            vlink.set("endpoints", "%s %s" % (fromid, toid))
-            self.add_attribute(vlink, "kbps", kbps)
-
-
-    def remove_vlink(self, endpoints, network=None):
-        vlinks = self.query_vlinks(endpoints, network)
-        for vlink in vlinks:
-            vlink.getparent().remove(vlink)
-
-
-    def merge(self, in_rspec):
-        """
-        Merge contents for specified rspec with current rspec 
-        """
-
-        from sfa.rspecs.rspec_parser import parse_rspec
-        rspec = parse_rspec(in_rspec)
-        if rspec.type.lower() == 'protogeni':
-            from sfa.rspecs.rspec_converter import RSpecConverter
-            in_rspec = RSpecConverter.to_sfa_rspec(in_rspec)
-            
-        # just copy over all networks
-        current_networks = self.get_networks()
-        rspec = SfaRSpec(rspec=in_rspec)
-        networks = rspec.get_network_elements()
-        for network in networks:
-            current_network = network.get('name')
-            if current_network and current_network not in current_networks:
-                self.xml.append(network)
-                current_networks.append(current_network)
-            
-        
-         
-
-if __name__ == '__main__':
-    rspec = SfaRSpec()
-    nodes = [
-    {'network': 'plc',
-     'hostname': 'node1.planet-lab.org',
-     'site_urn': 'urn:publicid:IDN+plc+authority+cm',
-      'node_id': 1,
-    },
-    {'network': 'plc',
-     'hostname': 'node2.planet-lab.org',
-     'site_urn': 'urn:publicid:IDN+plc+authority+cm',
-      'node_id': 1,
-    },
-    {'network': 'ple',
-     'hostname': 'node1.planet-lab.eu',
-     'site_urn': 'urn:publicid:IDN+plc+authority+cm',
-      'node_id': 1,
-    },
-    ]
-    rspec.add_nodes(nodes)
-    print rspec
index 972835f..6ba56c1 100755 (executable)
@@ -3,56 +3,82 @@
 from lxml import etree
 from StringIO import StringIO
 from sfa.util.xrn import *
-from sfa.rspecs.sfa_rspec import SfaRSpec
-from sfa.rspecs.pg_rspec import PGRSpec
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.version_manager import VersionManager
 
 class SfaRSpecConverter:
 
     @staticmethod
-    def to_pg_rspec(rspec):
-        if isinstance(rspec, SfaRSpec):
-            sfa_rspec = rspec
+    def to_pg_rspec(rspec, content_type = None):
+        if not isinstance(rspec, RSpec):
+            sfa_rspec = RSpec(rspec)
         else:
-            sfa_rspec = SfaRSpec(rspec=rspec)
-        pg_rspec = PGRSpec()
-    
+            sfa_rspec = rspec
+  
+        if not content_type or content_type not in \
+          ['ad', 'request', 'manifest']:
+            content_type = sfa_rspec.version.content_type
+     
+        version_manager = VersionManager()
+        pg_version = version_manager._get_version('protogeni', '2', 'request')
+        pg_rspec = RSpec(version=pg_version)
         # get networks
-        networks = sfa_rspec.get_networks()
+        networks = sfa_rspec.version.get_networks()
         
         for network in networks:
             # get nodes
-            sfa_node_elements = sfa_rspec.get_node_elements(network=network)
+            sfa_node_elements = sfa_rspec.version.get_node_elements(network=network)
             for sfa_node_element in sfa_node_elements:
                 # create node element
                 node_attrs = {}
                 node_attrs['exclusive'] = 'false'
-                node_attrs['component_manager_id'] = network
-                if sfa_node_element.find('hostname') != None:
-                    node_attrs['component_name'] = sfa_node_element.find('hostname').text
-                if sfa_node_element.find('urn') != None:    
-                    node_attrs['component_id'] = sfa_node_element.find('urn').text
-                node_element = pg_rspec.add_element('node', node_attrs)
+                if 'component_manager_id' in sfa_node_element.attrib:
+                    node_attrs['component_manager_id'] = sfa_node_element.attrib['component_manager_id']
+                else:
+                    node_attrs['component_manager_id'] = hrn_to_urn(network, 'authority+cm')
 
-                # create node_type element
-                for hw_type in ['plab-pc', 'pc']:
-                    hdware_type_element = pg_rspec.add_element('hardware_type', {'name': hw_type}, parent=node_element)
-                # create available element
-                pg_rspec.add_element('available', {'now': 'true'}, parent=node_element)
-                # create locaiton element
-                # We don't actually associate nodes with a country. 
-                # Set country to "unknown" until we figure out how to make
-                # sure this value is always accurate.
-                location = sfa_node_element.find('location')
-                if location != None:
-                    location_attrs = {}      
-                    location_attrs['country'] =  location.get('country', 'unknown')
-                    location_attrs['latitude'] = location.get('latitiue', 'None')
-                    location_attrs['longitude'] = location.get('longitude', 'None')
-                    pg_rspec.add_element('location', location_attrs, parent=node_element)
+                if 'component_id' in sfa_node_element.attrib:
+                    node_attrs['compoenent_id'] = sfa_node_element.attrib['component_id']
 
-                sliver_element = sfa_node_element.find('sliver')
-                if sliver_element != None:
-                    pg_rspec.add_element('sliver_type', {'name': 'planetlab-vnode'}, parent=node_element)
+                if sfa_node_element.find('hostname') != None:
+                    hostname = sfa_node_element.find('hostname').text
+                    node_attrs['component_name'] = hostname
+                    node_attrs['client_id'] = hostname
+                node_element = pg_rspec.xml.add_element('node', node_attrs)    
+            
+                if content_type == 'request':
+                    sliver_element = sfa_node_element.find('sliver')
+                    sliver_type_elements = sfa_node_element.xpath('./sliver_type', namespaces=sfa_rspec.namespaces)
+                    available_sliver_types = [element.attrib['name'] for element in sliver_type_elements]
+                    valid_sliver_types = ['emulab-openvz', 'raw-pc']
+                   
+                    # determine sliver type 
+                    requested_sliver_type = 'emulab-openvz'
+                    for available_sliver_type in available_sliver_types:
+                        if available_sliver_type in valid_sliver_types:
+                            requested_sliver_type = available_sliver_type
+                                
+                    if sliver_element != None:
+                        pg_rspec.xml.add_element('sliver_type', {'name': requested_sliver_type}, parent=node_element) 
+                else:
+                    # create node_type element
+                    for hw_type in ['plab-pc', 'pc']:
+                        hdware_type_element = pg_rspec.xml.add_element('hardware_type', {'name': hw_type}, parent=node_element)
+                    # create available element
+                    pg_rspec.xml.add_element('available', {'now': 'true'}, parent=node_element)
+                    # create locaiton element
+                    # We don't actually associate nodes with a country. 
+                    # Set country to "unknown" until we figure out how to make
+                    # sure this value is always accurate.
+                    location = sfa_node_element.find('location')
+                    if location != None:
+                        location_attrs = {}      
+                        location_attrs['country'] =  location.get('country', 'unknown')
+                        location_attrs['latitude'] = location.get('latitude', 'None')
+                        location_attrs['longitude'] = location.get('longitude', 'None')
+                        pg_rspec.xml.add_element('location', location_attrs, parent=node_element)
 
         return pg_rspec.toxml()
 
index bbfba68..f53ec6f 100644 (file)
@@ -1,5 +1,6 @@
 import os
 from sfa.util.faults import InvalidRSpec
+from sfa.rspecs.rspec_version import BaseVersion 
 from sfa.util.sfalogging import logger    
 
 class VersionManager:
@@ -23,15 +24,16 @@ class VersionManager:
             module = __import__(module_path, fromlist=module_path)
             for attr_name in dir(module):
                 attr = getattr(module, attr_name)
-                if hasattr(attr, 'version'):
-                    self.versions.append(attr)
+                if hasattr(attr, 'version') and hasattr(attr, 'enabled') and attr.enabled == True:
+                    self.versions.append(attr())
 
     def _get_version(self, type, version_num=None, content_type=None):
         retval = None
         for version in self.versions:
             if type is None or type.lower() == version.type.lower():
                 if version_num is None or version_num == version.version:
-                    if content_type is None or content_type.lower() == version.content_type.lower():
+                    if content_type is None or content_type.lower() == version.content_type.lower() \
+                      or version.content_type == '*':
                         retval = version
         if not retval:
             raise InvalidRSpec("No such version format: %s version: %s type:%s "% (type, version_num, content_type))
@@ -40,7 +42,7 @@ class VersionManager:
     def get_version(self, version=None):
         retval = None
         if isinstance(version, dict):
-            retval =  self._get_version(version.get('type'), version.get('version_num'), version.get('content_type'))
+            retval =  self._get_version(version.get('type'), version.get('version'), version.get('content_type'))
         elif isinstance(version, basestring):
             version_parts = version.split(' ')     
             num_parts = len(version_parts)
@@ -52,8 +54,9 @@ class VersionManager:
             if num_parts > 2:
                 content_type = version_parts[2]
             retval = self._get_version(type, version_num, content_type) 
+        elif isinstance(version, BaseVersion):
+            retval = version
         else:
-            logger.info("Unable to parse rspec version, using default")
             retval = self._get_version(self.default_type, self.default_version_num)   
  
         return retval
index 162a1c4..8da0119 100644 (file)
+from lxml import etree
+from copy import deepcopy
+from StringIO import StringIO
+from sfa.util.xrn import *
+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
 
-class PGv2:
+class PGv2(BaseVersion):
     type = 'ProtoGENI'
-    content_type = '*'
+    content_type = 'ad'
     version = '2'
-    schema = 'http://www.protogeni.net/resources/rspec/2/*.xsd' 
-    namespaces = { 
-        'default' : 'http://www.protogeni.net/resources/rspec/2',
+    schema = 'http://www.protogeni.net/resources/rspec/2/ad.xsd'
+    namespace = 'http://www.protogeni.net/resources/rspec/2'
+    extensions = {
         'flack': "http://www.protogeni.net/resources/rspec/ext/flack/1",
-        'planetlab': "http://www.planet-lab.org/resources/sfa/ext/planetlab/1", 
+        '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'),
     ]
-    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.protogeni.net/resources/rspec/2" xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2 http://www.protogeni.net/resources/rspec/2/%s.xsd" xmlns:flack="http://www.protogeni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'      
 
+    def get_network(self):
+        network = None
+        nodes = self.xml.xpath('//default:node[@component_manager_id][1]', namespaces=self.namespaces)
+        if nodes:
+            network  = nodes[0].get('component_manager_id')
+        return network
+
+    def get_networks(self):
+        networks = self.xml.xpath('//default:node[@component_manager_id]/@component_manager_id', namespaces=self.namespaces)
+        return set(networks)
+
+    def get_node_element(self, hostname, network=None):
+        nodes = self.xml.xpath('//default:node[@component_id[contains(., "%s")]] | node[@component_id[contains(., "%s")]]' % (hostname, hostname), namespaces=self.namespaces)
+        if isinstance(nodes,list) and nodes:
+            return nodes[0]
+        else:
+            return None
+
+    def get_node_elements(self, network=None):
+        nodes = self.xml.xpath('//default:node | //node', namespaces=self.namespaces)
+        return nodes
+
+
+    def get_nodes(self, network=None):
+        xpath = '//default:node[@component_name]/@component_id | //node[@component_name]/@component_id'
+        nodes = self.xml.xpath(xpath, namespaces=self.namespaces)
+        nodes = [xrn_to_hostname(node) for node in nodes]
+        return nodes
+
+    def get_nodes_with_slivers(self, network=None):
+        if network:
+            nodes = self.xml.xpath('//default:node[@component_manager_id="%s"][sliver_type]/@component_id' % network, namespaces=self.namespaces)
+        else:
+            nodes = self.xml.xpath('//default:node[default:sliver_type]/@component_id', namespaces=self.namespaces)
+        nodes = [xrn_to_hostname(node) for node in nodes]
+        return nodes
+
+    def get_nodes_without_slivers(self, network=None):
+        return []
+
+    def get_sliver_attributes(self, hostname, network=None):
+        node = self.get_node_element(hostname, network)
+        sliver = node.xpath('./default:sliver_type', namespaces=self.namespaces)
+        if sliver is not None and isinstance(sliver, list):
+            sliver = sliver[0]
+        return self.attributes_list(sliver)
+
+    def get_slice_attributes(self, network=None):
+        slice_attributes = []
+        nodes_with_slivers = self.get_nodes_with_slivers(network)
+        # TODO: default sliver attributes in the PG rspec?
+        default_ns_prefix = self.namespaces['default']
+        for node in nodes_with_slivers:
+            sliver_attributes = self.get_sliver_attributes(node, network)
+            for sliver_attribute in sliver_attributes:
+                name=str(sliver_attribute[0])
+                text =str(sliver_attribute[1])
+                attribs = sliver_attribute[2]
+                # we currently only suppor the <initscript> and <flack> attributes
+                if  'info' in name:
+                    attribute = {'name': 'flack_info', 'value': str(attribs), 'node_id': node}
+                    slice_attributes.append(attribute)
+                elif 'initscript' in name:
+                    if attribs is not None and 'name' in attribs:
+                        value = attribs['name']
+                    else:
+                        value = text
+                    attribute = {'name': 'initscript', 'value': value, 'node_id': node}
+                    slice_attributes.append(attribute)
+
+        return slice_attributes
+
+    def attributes_list(self, elem):
+        opts = []
+        if elem is not None:
+            for e in elem:
+                opts.append((e.tag, str(e.text).strip(), e.attrib))
+        return opts
+
+    def get_default_sliver_attributes(self, network=None):
+        return []
+
+    def add_default_sliver_attribute(self, name, value, network=None):
+        pass
+
+    def add_nodes(self, nodes, check_for_dupes=False):
+        if not isinstance(nodes, list):
+            nodes = [nodes]
+        for node in nodes:
+            urn = ""
+            if check_for_dupes and \
+              self.xml.xpath('//default:node[@component_uuid="%s"]' % urn, namespaces=self.namespaces):
+                # node already exists
+                continue
+
+            node_tag = etree.SubElement(self.xml.root, 'node', exclusive='false')
+            if 'network_urn' in node:
+                node_tag.set('component_manager_id', node['network_urn'])
+            if 'urn' in node:
+                node_tag.set('component_id', node['urn'])
+            if 'hostname' in node:
+                node_tag.set('component_name', node['hostname'])
+            # TODO: should replace plab-pc with pc model
+            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')
+
+            pl_initscripts = node.get('pl_initscripts', {})
+            for pl_initscript in pl_initscripts.values():
+                etree.SubElement(sliver_type_tag, '{%s}initscript' % self.namespaces['planetlab'], name=pl_initscript['name'])
+
+            # protogeni uses the <sliver_type> tag to identify the types of
+            # vms available at the node.
+            # only add location tag if longitude and latitude are not null
+            if 'site' in node:
+                longitude = node['site'].get('longitude', None)
+                latitude = node['site'].get('latitude', None)
+                if longitude and latitude:
+                    location_tag = etree.SubElement(node_tag, 'location', country="us", \
+                                                    longitude=str(longitude), latitude=str(latitude))
+
+    def merge_node(self, source_node_tag):
+        # this is untested
+        self.xml.root.append(deepcopy(source_node_tag))
+
+    def add_slivers(self, slivers, sliver_urn=None, no_dupes=False):
+
+        # all nodes hould already be present in the rspec. Remove all
+        # nodes that done have slivers
+        slivers_dict = {}
+        for sliver in slivers:
+            if isinstance(sliver, basestring):
+                slivers_dict[sliver] = {'hostname': sliver}
+            elif isinstance(sliver, dict):
+                slivers_dict[sliver['hostname']] = sliver        
+
+        nodes = self.get_node_elements()
+        for node in nodes:
+            urn = node.get('component_id')
+            hostname = xrn_to_hostname(urn)
+            if hostname not in slivers_dict:
+                parent = node.getparent()
+                parent.remove(node)
+            else:
+                sliver_info = slivers_dict[hostname]
+                sliver_type_elements = node.xpath('./sliver_type', namespaces=self.namespaces)
+                available_sliver_types = [element.attrib['name'] for element in sliver_type_elements]
+                valid_sliver_types = ['emulab-openvz', 'raw-pc']
+                requested_sliver_type = None
+                for valid_sliver_type in valid_sliver_types:
+                    if valid_sliver_type in available_sliver_type:
+                        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)
+                    if sliver_elem and isinstance(sliver_elem, list):
+                        sliver_elem = sliver_elem[0]
+                        node.remove(sliver_elem)
+                    # set the client id
+                    node.set('client_id', hostname)
+                    if sliver_urn:
+                        # set the sliver id
+                        slice_id = sliver_info.get('slice_id', -1)
+                        node_id = sliver_info.get('node_id', -1)
+                        sliver_id = urn_to_sliver_id(sliver_urn, slice_id, node_id)
+                        node.set('sliver_id', sliver_id)
+
+                    # add the sliver element
+                    sliver_elem = etree.SubElement(node, 'sliver_type', name=requested_sliver_type)
+                    for tag in sliver_info.get('tags', []):
+                        if tag['tagname'] == 'flack_info':
+                            e = etree.SubElement(sliver_elem, '{%s}info' % self.namespaces['flack'], attrib=eval(tag['value']))
+                        elif tag['tagname'] == 'initscript':
+                            e = etree.SubElement(sliver_elem, '{%s}initscript' % self.namespaces['planetlab'], attrib={'name': tag['value']})                
+                else:
+                    # node isn't usable. just remove it from the request     
+                    parent = node.getparent()
+                    parent.remove(node)
+
+
+    def remove_slivers(self, slivers, network=None, no_dupes=False):
+        for sliver in slivers:
+            node_elem = self.get_node_element(sliver['hostname'])
+            sliver_elem = node_elem.xpath('./default:sliver_type', self.namespaces)
+            if sliver_elem != None and sliver_elem != []:
+                node_elem.remove(sliver_elem[0])
+
+    def add_default_sliver_attribute(self, name, value, network=None):
+        pass
+
+    def add_interfaces(self, interfaces, no_dupes=False):
+        pass
+
+    def add_links(self, links, no_dupes=False):
+        pass
+
+    def merge(self, in_rspec):
+        """
+        Merge contents for specified rspec with current rspec
+        """
+        from sfa.rspecs.rspec import RSpec
+        # just copy over all the child elements under the root element
+        if isinstance(in_rspec, RSpec):
+            in_rspec = in_rspec.toxml()
+        tree = etree.parse(StringIO(in_rspec))
+        root = tree.getroot()
+        for child in root.getchildren():
+            self.xml.root.append(child)
+
+    def cleanup(self):
+        # remove unncecessary elements, attributes
+        if self.type in ['request', 'manifest']:
+            # remove 'available' element from remaining node elements
+            self.xml.remove_element('//default:available | //available')
 
 class PGv2Ad(PGv2):
-    schema = 'http://www.protogeni.net/resources/rspec/2/ad.xsd'
+    enabled = True
     content_type = 'ad'
-    template = PGv2.template % content_type
+    schema = 'http://www.protogeni.net/resources/rspec/2/ad.xsd'
+    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.protogeni.net/resources/rspec/2" xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2 http://www.protogeni.net/resources/rspec/2/ad.xsd" xmlns:flack="http://www.protogeni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'
+
+class PGv2Request(PGv2):
+    enabled = True
+    content_type = 'request'
+    schema = 'http://www.protogeni.net/resources/rspec/2/request.xsd'
+    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.protogeni.net/resources/rspec/2" xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2 http://www.protogeni.net/resources/rspec/2/request.xsd" xmlns:flack="http://www.protogeni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'
 
 class PGv2Manifest(PGv2):
-    schema = 'http://www.protogeni.net/resources/rspec/2/manifest.xsd'
+    enabled = True
     content_type = 'manifest'
-    template = PGv2.template % content_type
+    schema = 'http://www.protogeni.net/resources/rspec/2/manifest.xsd'
+    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.protogeni.net/resources/rspec/2" xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2 http://www.protogeni.net/resources/rspec/2/manifest.xsd" xmlns:flack="http://www.protogeni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'
+     
+
 
 if __name__ == '__main__':
     from sfa.rspecs.rspec import RSpec
diff --git a/sfa/rspecs/versions/pgv3.py b/sfa/rspecs/versions/pgv3.py
new file mode 100644 (file)
index 0000000..2bf84bb
--- /dev/null
@@ -0,0 +1,34 @@
+from sfa.rspecs.versions.pgv2 import PGv2
+
+class PGv3(PGv2):
+    type = 'GENI'
+    content_type = 'ad'
+    version = '3'
+    schema = 'http://www.geni.net/resources/rspec/3/ad.xsd'
+    namespace = 'http://www.geni.net/resources/rspec/3'
+    extensions = {
+        'flack': "http://www.geni.net/resources/rspec/ext/flack/1",
+        'planetlab': "http://www.planet-lab.org/resources/sfa/ext/planetlab/1",
+    }
+    namespaces = dict(extensions.items() + [('default', namespace)])
+    elements = []
+
+
+class PGv3Ad(PGv3):
+    enabled = True
+    content_type = 'ad'
+    schema = 'http://www.geni.net/resources/rspec/3/ad.xsd'
+    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.geni.net/resources/rspec/3" xsi:schemaLocation="http://www.geni.net/resources/rspec/3 http://www.geni.net/resources/rspec/3/ad.xsd" xmlns:flack="http://www.geni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'
+
+class PGv3Request(PGv3):
+    enabled = True
+    content_type = 'request'
+    schema = 'http://www.geni.net/resources/rspec/3/request.xsd'
+    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.geni.net/resources/rspec/3" xsi:schemaLocation="http://www.geni.net/resources/rspec/3 http://www.geni.net/resources/rspec/3/request.xsd" xmlns:flack="http://www.geni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'
+
+class PGv2Manifest(PGv3):
+    enabled = True
+    content_type = 'manifest'
+    schema = 'http://www.geni.net/resources/rspec/3/manifest.xsd'
+    template = '<rspec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.geni.net/resources/rspec/3" xsi:schemaLocation="http://www.geni.net/resources/rspec/3 http://www.geni.net/resources/rspec/3/manifest.xsd" xmlns:flack="http://www.geni.net/resources/rspec/ext/flack/1" xmlns:planetlab="http://www.planet-lab.org/resources/sfa/ext/planetlab/1" />'
+     
index 648e8d3..a9c3844 100644 (file)
@@ -1,11 +1,17 @@
+from lxml import etree
+from sfa.util.xrn import hrn_to_urn, urn_to_hrn
+from sfa.rspecs.rspec_version import BaseVersion
 from sfa.rspecs.rspec_elements import RSpecElement, RSpecElements
 
-class SFAv1:
+class SFAv1(BaseVersion):
+    enabled = True
     type = 'SFA'
     content_type = '*'
     version = '1'
     schema = None
-    namespaces = {}
+    namespace = None
+    extensions = {}
+    namespaces = None
     elements = [
         RSpecElement(RSpecElements.NETWORK, 'network', '//network'),
         RSpecElement(RSpecElements.NODE, 'node', '//node'),
@@ -13,6 +19,344 @@ class SFAv1:
     ] 
     template = '<RSpec type="%s"></RSpec>' % type
 
+    def get_network_elements(self):
+        return self.xml.xpath('//network')
+
+    def get_networks(self):
+        return self.xml.xpath('//network[@name]/@name')
+
+    def get_node_element(self, hostname, network=None):
+        if network:
+            names = self.xml.xpath('//network[@name="%s"]//node/hostname' % network)
+        else:
+            names = self.xml.xpath('//node/hostname')
+        for name in names:
+            if str(name.text).strip() == hostname:
+                return name.getparent()
+        return None
+
+    def get_node_elements(self, network=None):
+        if network:
+            return self.xml.xpath('//network[@name="%s"]//node' % network)
+        else:
+            return self.xml.xpath('//node')
+
+    def get_nodes(self, network=None):
+        if network == None:
+            nodes = self.xml.xpath('//node/hostname/text()')
+        else:
+            nodes = self.xml.xpath('//network[@name="%s"]//node/hostname/text()' % network)
+
+        nodes = [node.strip() for node in nodes]
+        return nodes
+
+    def get_nodes_with_slivers(self, network = None):
+        if network:
+            nodes =  self.xml.xpath('//network[@name="%s"]//node[sliver]/hostname/text()' % network)  
+        else:
+            nodes = self.xml.xpath('//node[sliver]/hostname/text()')
+
+        nodes = [node.strip() for node in nodes]
+        return nodes
+
+    def get_nodes_without_slivers(self, network=None):
+        xpath_nodes_without_slivers = '//node[not(sliver)]/hostname/text()'
+        xpath_nodes_without_slivers_in_network = '//network[@name="%s"]//node[not(sliver)]/hostname/text()'
+        if network:
+            return self.xml.xpath('//network[@name="%s"]//node[not(sliver)]/hostname/text()' % network)
+        else:
+            return self.xml.xpath('//node[not(sliver)]/hostname/text()')
+
+    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_default_sliver_attributes(self, network=None):
+        if network:
+            defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
+        else:
+            defaults = self.xml.xpath("//sliver_defaults")
+        if isinstance(defaults, list) and defaults:
+            defaults = defaults[0]
+        return self.attributes_list(defaults)
+
+    def get_sliver_attributes(self, hostname, network=None):
+        attributes = []
+        node = self.get_node_element(hostname, network)
+        #sliver = node.find("sliver")
+        slivers = node.xpath('./sliver')
+        if isinstance(slivers, list) and slivers:
+            attributes = self.attributes_list(slivers[0])
+        return attributes
+
+    def get_slice_attributes(self, network=None):
+        slice_attributes = []
+        nodes_with_slivers = self.get_nodes_with_slivers(network)
+        for default_attribute in self.get_default_sliver_attributes(network):
+            attribute = {'name': str(default_attribute[0]), 'value': str(default_attribute[1]), 'node_id': None}
+            slice_attributes.append(attribute)
+        for node in nodes_with_slivers:
+            sliver_attributes = self.get_sliver_attributes(node, network)
+            for sliver_attribute in sliver_attributes:
+                attribute = {'name': str(sliver_attribute[0]), 'value': str(sliver_attribute[1]), 'node_id': node}
+                slice_attributes.append(attribute)
+        return slice_attributes
+
+    def get_site_nodes(self, siteid, network=None):
+        if network:
+            nodes = self.xml.xpath('//network[@name="%s"]/site[@id="%s"]/node/hostname/text()' % \
+                                    (network, siteid))
+        else:
+            nodes = self.xml.xpath('//site[@id="%s"]/node/hostname/text()' % siteid)
+        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
+
+    def get_link(self, fromnode, tonode, network=None):
+        fromsite = fromnode.getparent()
+        tosite = tonode.getparent()
+        fromid = fromsite.get("id")
+        toid = tosite.get("id")
+        if network:
+            query = "//network[@name='%s']" % network + "/link[@endpoints = '%s %s']"
+        else:
+            query = "//link[@endpoints = '%s %s']"
+
+        results = self.rspec.xpath(query % (fromid, toid))
+        if not results:
+            results = self.rspec.xpath(query % (toid, fromid))
+        return results
+
+    def query_links(self, fromnode, tonode, network=None):
+        return get_link(fromnode, tonode, network)
+
+    def get_vlinks(self, network=None):
+        vlinklist = []
+        if network:
+            vlinks = self.xml.xpath("//network[@name='%s']//vlink" % network)
+        else:
+            vlinks = self.xml.xpath("//vlink")
+        for vlink in vlinks:
+            endpoints = vlink.get("endpoints")
+            (end1, end2) = endpoints.split()
+            if network:
+                node1 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
+                                       (network, end1))[0]
+                node2 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
+                                       (network, end2))[0]
+            else:
+                node1 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end1)[0]
+                node2 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end2)[0]
+            desc = "%s <--> %s" % (node1, node2)
+            kbps = vlink.find("kbps")
+            vlinklist.append((endpoints, desc, kbps.text))
+        return vlinklist
+
+    def get_vlink(self, endponts, network=None):
+        if network:
+            query = "//network[@name='%s']//vlink[@endpoints = '%s']" % (network, endpoints)
+        else:
+            query = "//vlink[@endpoints = '%s']" % (network, endpoints)
+        results = self.rspec.xpath(query)
+        return results
+
+    def query_vlinks(self, endpoints, network=None):
+        return get_vlink(endpoints,network)
+
+    
+    ##################
+    # Builder
+    ##################
+
+    def add_network(self, network):
+        network_tags = self.xml.xpath('//network[@name="%s"]' % network)
+        if not network_tags:
+            network_tag = etree.SubElement(self.xml.root, 'network', name=network)
+        else:
+            network_tag = network_tags[0]
+        return network_tag
+
+    def add_nodes(self, nodes, network = None, no_dupes=False):
+        if not isinstance(nodes, list):
+            nodes = [nodes]
+        for node in nodes:
+            if no_dupes and \
+              self.get_node_element(node['hostname']):
+                # node already exists
+                continue
+
+            network_tag = self.xml.root
+            if 'network' in node:
+                network = node['network']
+                network_tag = self.add_network(network)
+
+            node_tag = etree.SubElement(network_tag, 'node')
+            if 'network' in node:
+                node_tag.set('component_manager_id', hrn_to_urn(network, 'authority+sa'))
+            if 'urn' in node:
+                node_tag.set('component_id', node['urn'])
+            if 'site_urn' in node:
+                node_tag.set('site_id', node['site_urn'])
+            if 'node_id' in node:
+                node_tag.set('node_id', 'n'+str(node['node_id']))
+            if 'boot_state' in node:
+                node_tag.set('boot_state', node['boot_state'])
+            if 'hostname' in node:
+                hostname_tag = etree.SubElement(node_tag, 'hostname').text = node['hostname']
+            if 'interfaces' in node:
+                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)
+            if 'tags' in node:
+                for tag in node['tags']:
+                   # expose this hard wired list of tags, plus the ones that are marked 'sfa' in their category
+                   if tag['tagname'] in ['fcdistro', 'arch'] or 'sfa' in tag['category'].split('/'):
+                        tag_element = etree.SubElement(node_tag, tag['tagname']).text=tag['value']
+
+            if 'site' in node:
+                longitude = str(node['site']['longitude'])
+                latitude = str(node['site']['latitude'])
+                location = etree.SubElement(node_tag, 'location', country='unknown', \
+                                            longitude=longitude, latitude=latitude)
+
+    def merge_node(self, source_node_tag, network, no_dupes=False):
+        if no_dupes and self.get_node_element(node['hostname']):
+            # node already exists
+            return
+
+        network_tag = self.add_network(network)
+        network_tag.append(deepcopy(source_node_tag))
+
+    def add_interfaces(self, interfaces):
+        pass
+
+    def add_links(self, links):
+        pass
+
+    def add_slivers(self, slivers, network=None, sliver_urn=None, no_dupes=False):
+        # add slice name to network tag
+        network_tags = self.xml.xpath('//network')
+        if network_tags:
+            network_tag = network_tags[0]
+            network_tag.set('slice', urn_to_hrn(sliver_urn)[0])
+        
+        all_nodes = self.get_nodes()
+        nodes_with_slivers = [sliver['hostname'] for sliver in slivers]
+        nodes_without_slivers = set(all_nodes).difference(nodes_with_slivers)
+        
+        # add slivers
+        for sliver in slivers:
+            node_elem = self.get_node_element(sliver['hostname'], network)
+            if not node_elem: continue
+            sliver_elem = etree.SubElement(node_elem, 'sliver')
+            if 'tags' in sliver:
+                for tag in sliver['tags']:
+                    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)
+
+    def remove_slivers(self, slivers, network=None, no_dupes=False):
+        for sliver in slivers:
+            node_elem = self.get_node_element(sliver['hostname'], network)
+            sliver_elem = node_elem.find('sliver')
+            if sliver_elem != None:
+                node_elem.remove(sliver_elem)
+
+    def add_default_sliver_attribute(self, name, value, network=None):
+        if network:
+            defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
+        else:
+            defaults = self.xml.xpath("//sliver_defaults" % network)
+        if not defaults :
+            network_tag = self.xml.xpath("//network[@name='%s']" % network)
+            if isinstance(network_tag, list):
+                network_tag = network_tag[0]
+            defaults = self.xml.add_element('sliver_defaults', attrs={}, parent=network_tag)
+        elif isinstance(defaults, list):
+            defaults = defaults[0]
+        self.xml.add_attribute(defaults, name, value)
+
+    def add_sliver_attribute(self, hostname, name, value, network=None):
+        node = self.get_node_element(hostname, network)
+        sliver = node.find("sliver")
+        self.xml.add_attribute(sliver, name, value)
+
+    def remove_default_sliver_attribute(self, name, value, network=None):
+        if network:
+            defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
+        else:
+            defaults = self.xml.xpath("//sliver_defaults" % network)
+        self.xml.remove_attribute(defaults, name, value)
+
+    def remove_sliver_attribute(self, hostname, name, value, network=None):
+        node = self.get_node_element(hostname, network)
+        sliver = node.find("sliver")
+        self.xml.remove_attribute(sliver, name, value)
+
+    def add_vlink(self, fromhost, tohost, kbps, network=None):
+        fromnode = self.get_node_element(fromhost, network)
+        tonode = self.get_node_element(tohost, network)
+        links = self.get_link(fromnode, tonode, network)
+
+        for link in links:
+            vlink = etree.SubElement(link, "vlink")
+            fromid = fromnode.get("id")
+            toid = tonode.get("id")
+            vlink.set("endpoints", "%s %s" % (fromid, toid))
+            self.xml.add_attribute(vlink, "kbps", kbps)
+
+
+    def remove_vlink(self, endpoints, network=None):
+        vlinks = self.query_vlinks(endpoints, network)
+        for vlink in vlinks:
+            vlink.getparent().remove(vlink)
+
+
+    def merge(self, in_rspec):
+        """
+        Merge contents for specified rspec with current rspec
+        """
+
+        from sfa.rspecs.rspec import RSpec
+        if isinstance(in_rspec, RSpec):
+            rspec = in_rspec
+        else:
+            rspec = RSpec(in_rspec)
+        if rspec.version.type.lower() == 'protogeni':
+            from sfa.rspecs.rspec_converter import RSpecConverter
+            in_rspec = RSpecConverter.to_sfa_rspec(rspec.toxml())
+            rspec = RSpec(in_rspec)
+
+        # just copy over all networks
+        current_networks = self.get_networks()
+        networks = rspec.version.get_network_elements()
+        for network in networks:
+            current_network = network.get('name')
+            if current_network and current_network not in current_networks:
+                self.xml.root.append(network)
+                current_networks.append(current_network)
+
 if __name__ == '__main__':
     from sfa.rspecs.rspec import RSpec
     from sfa.rspecs.rspec_elements import *
diff --git a/sfa/rspecs/xml.py b/sfa/rspecs/xml.py
new file mode 100755 (executable)
index 0000000..ac6526c
--- /dev/null
@@ -0,0 +1,193 @@
+#!/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 XML:
+    def __init__(self, xml=None):
+        self.root = None
+        self.namespaces = None
+        self.default_namespace = None
+        self.schema = None
+        if isinstance(xml, basestring):
+            self.parse_xml(xml)
+        elif isinstance(xml, etree._ElementTree):
+            self.root = xml.getroot()
+        elif isinstance(xml, etree._Element):
+            self.root = xml 
+
+    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.root = tree.getroot()
+        # set namespaces map
+        self.namespaces = dict(self.root.nsmap)
+        # If the 'None' exist, then it's pointing to the default namespace. This makes 
+        # it hard for us to write xpath queries for the default naemspace because lxml 
+        # wont understand a None prefix. We will just associate the default namespeace 
+        # with a key named 'default'.     
+        if None in self.namespaces:
+            default_namespace = self.namespaces.pop(None)
+            self.namespaces['default'] = default_namespace
+
+        # set schema 
+        for key in self.root.attrib.keys():
+            if key.endswith('schemaLocation'):
+                # schema location should be at the end of the list
+                schema_parts  = self.root.attrib[key].split(' ')
+                self.schema = schema_parts[1]    
+                namespace, schema  = schema_parts[0], schema_parts[1]
+                break
+
+    def validate(self, schema):
+        """
+        Validate against rng schema
+        """
+        relaxng_doc = etree.parse(schema)
+        relaxng = etree.RelaxNG(relaxng_doc)
+        if not relaxng(self.root):
+            error = relaxng.error_log.last_error
+            message = "%s (line %s)" % (error.message, error.line)
+            raise InvalidRSpec(message)
+        return True
+
+    def xpath(self, xpath, namespaces=None):
+        if not namespaces:
+            namespaces = self.namespaces
+        return self.root.xpath(xpath, namespaces=namespaces)
+
+    def set(self, key, value):
+        return self.root.set(key, value)
+
+    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.root
+        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.root
+
+        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 __str__(self):
+        return self.toxml()
+
+    def toxml(self):
+        return etree.tostring(self.root, 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 0faca69..98fe416 100755 (executable)
@@ -29,8 +29,8 @@ class XpathFilter:
 class XMLInterface:
  
     def __init__(self, xml=""):
-        self.header = '<?xml version="1.0"?>\n'
-        self.template = """<RSpec></RSpec>"""
+        self.header = None 
+        self.template = None 
         self.xml = None
         self.namespaces = None
         if xml: