Reservation-oriented Rspec with planetlab
authorMohamed Larabi <mohamed.larabi@inria.fr>
Mon, 4 Jun 2012 18:14:21 +0000 (20:14 +0200)
committerMohamed Larabi <mohamed.larabi@inria.fr>
Mon, 4 Jun 2012 18:14:21 +0000 (20:14 +0200)
sfa/client/sfi.py
sfa/planetlab/plaggregate.py
sfa/planetlab/pldriver.py
sfa/planetlab/plshell.py
sfa/planetlab/plslices.py
sfa/rspecs/elements/granularity.py [new file with mode: 0644]
sfa/rspecs/elements/lease.py [new file with mode: 0644]
sfa/rspecs/elements/versions/sfav1Lease.py [new file with mode: 0644]
sfa/rspecs/elements/versions/sfav1Node.py
sfa/rspecs/rspec_elements.py
sfa/rspecs/versions/sfav1.py

index 4387dba..26a7f54 100644 (file)
@@ -372,6 +372,9 @@ class Sfi:
             #panos: a new option to define the type of information about resources a user is interested in
             parser.add_option("-i", "--info", dest="info",
                                 help="optional component information", default=None)
+            # a new option to retreive or not reservation-oriented RSpecs (leases)
+            parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
+                                help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
 
 
         # 'create' does return the new rspec, makes sense to save that too
@@ -955,6 +958,8 @@ or with an slice hrn, shows currently provisioned resources
             api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
         if options.info:
             api_options['info'] = options.info
+        if options.list_leases:
+            api_options['list_leases'] = options.list_leases
         if options.current:
             if options.current == True:
                 api_options['cached'] = False
index ccf6852..e2ff3f9 100644 (file)
@@ -13,12 +13,15 @@ from sfa.rspecs.elements.location import Location
 from sfa.rspecs.elements.interface import Interface
 from sfa.rspecs.elements.services import Services
 from sfa.rspecs.elements.pltag import PLTag
+from sfa.rspecs.elements.lease import Lease
+from sfa.rspecs.elements.granularity import Granularity
 from sfa.rspecs.version_manager import VersionManager
 
-from sfa.planetlab.plxrn import PlXrn, hostname_to_urn, hrn_to_pl_slicename
+from sfa.planetlab.plxrn import PlXrn, hostname_to_urn, hrn_to_pl_slicename, slicename_to_hrn
 from sfa.planetlab.vlink import get_tc_rate
 from sfa.planetlab.topology import Topology
 
+import time
 
 class PlAggregate:
 
@@ -154,6 +157,9 @@ class PlAggregate:
         
         filter.update({'peer_id': None})
         nodes = self.driver.shell.GetNodes(filter)
+        
+        # get the granularity in second for the reservation system
+        grain = self.driver.shell.GetLeaseGranularity()
        
         site_ids = []
         interface_ids = []
@@ -205,6 +211,10 @@ class PlAggregate:
             if site['longitude'] and site['latitude']:  
                 location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'})
                 rspec_node['location'] = location
+            # Granularity
+            granularity = Granularity({'grain': grain})
+            rspec_node['granularity'] = granularity
+
             rspec_node['interfaces'] = []
             if_count=0
             for if_id in node['interface_ids']:
@@ -234,7 +244,44 @@ class PlAggregate:
             rspec_nodes.append(rspec_node)
         return (rspec_nodes, links)
              
+
+    def get_leases(self, slice=None, options={}):
         
+        now = int(time.time())
+        filter={}
+        filter.update({'clip':now})
+        if slice:
+           filter.update({'name':slice['name']})
+        return_fields = ['lease_id', 'hostname', 'site_id', 'name', 't_from', 't_until']
+        leases = self.driver.shell.GetLeases(filter)
+
+        site_ids = []
+        for lease in leases:
+            site_ids.append(lease['site_id'])
+
+        # get sites
+        sites_dict  = self.get_sites({'site_id': site_ids}) 
+  
+        rspec_leases = []
+        for lease in leases:
+
+            rspec_lease = Lease()
+            
+            # xxx how to retrieve site['login_base']
+            site_id=lease['site_id']
+            site=sites_dict[site_id]
+
+            rspec_lease['lease_id'] = lease['lease_id']
+            rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], lease['hostname'])
+            slice_hrn = slicename_to_hrn(self.driver.hrn, lease['name'])
+            slice_urn = hrn_to_urn(slice_hrn, 'slice')
+            rspec_lease['slice_id'] = slice_urn
+            rspec_lease['t_from'] = lease['t_from']
+            rspec_lease['t_until'] = lease['t_until']          
+            rspec_leases.append(rspec_lease)
+        return rspec_leases
+
+    
     def get_rspec(self, slice_xrn=None, version = None, options={}):
 
         version_manager = VersionManager()
@@ -249,17 +296,22 @@ class PlAggregate:
         if slice and 'expires' in slice:
             rspec.xml.set('expires',  datetime_to_string(utcparse(slice['expires'])))
 
-        nodes, links = self.get_nodes_and_links(slice_xrn, slice, slivers)
-        rspec.version.add_nodes(nodes)
-        rspec.version.add_links(links)
+        if not options.get('list_leases') or options.get('list_leases') and options['list_leases'] != 'leases':
+           nodes, links = self.get_nodes_and_links(slice_xrn, slice, slivers)
+           rspec.version.add_nodes(nodes)
+           rspec.version.add_links(links)
+           # add sliver defaults
+           default_sliver = slivers.get(None, [])
+           if default_sliver:
+              default_sliver_attribs = default_sliver.get('tags', [])
+              for attrib in default_sliver_attribs:
+                  logger.info(attrib)
+                  rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value'])
         
-        # add sliver defaults
-        default_sliver = slivers.get(None, [])
-        if default_sliver:
-            default_sliver_attribs = default_sliver.get('tags', [])
-            for attrib in default_sliver_attribs:
-                logger.info(attrib)
-                rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value'])
+        if not options.get('list_leases') or options.get('list_leases') and options['list_leases'] != 'resources':
+           leases = self.get_leases(slice)
+           rspec.version.add_leases(leases)
+
         return rspec.toxml()
 
 
index e4330e8..12d5410 100644 (file)
@@ -27,7 +27,7 @@ from sfa.planetlab.plshell import PlShell
 import sfa.planetlab.peers as peers
 from sfa.planetlab.plaggregate import PlAggregate
 from sfa.planetlab.plslices import PlSlices
-from sfa.planetlab.plxrn import PlXrn, slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename
+from sfa.planetlab.plxrn import PlXrn, slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_login_base, xrn_to_hostname
 
 
 def list_to_dict(recs, key):
@@ -611,6 +611,10 @@ class PlDriver (Driver):
         #panos adding the info option to the caching key (can be improved)
         if options.get('info'):
             version_string = version_string + "_"+options.get('info', 'default')
+
+        # Adding the list_leases option to the caching key
+        if options.get('list_leases'):
+            version_string = version_string + "_"+options.get('list_leases', 'default')
     
         # look in cache first
         if cached_requested and self.cache and not slice_hrn:
@@ -737,6 +741,22 @@ class PlDriver (Driver):
    
         # add/remove links links 
         slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
+
+        # add/remove leases
+        requested_leases = []
+        kept_leases = []
+        for lease in rspec.version.get_leases():
+            requested_lease = {}
+            if not lease.get('lease_id'):
+               requested_lease['hostname'] = xrn_to_hostname(lease.get('component_id').strip())
+               requested_lease['t_from'] = lease.get('t_from')
+               requested_lease['t_until'] = lease.get('t_until')
+            else:
+               kept_leases.append(int(lease['lease_id']))
+            if requested_lease.get('hostname'):
+                requested_leases.append(requested_lease)
+
+        leases = slices.verify_slice_leases(slice, requested_leases, kept_leases, peer)
     
         # handle MyPLC peer association.
         # only used by plc and ple.
index d2cd9cd..f42af88 100644 (file)
@@ -23,6 +23,9 @@ class PlShell:
                     'UpdateSlice', 'UpdateSliceTag',
                     # also used as-is in importer
                     'GetSites','GetNodes',
+                    # Lease management methods
+                    'GetLeases', 'GetLeaseGranularity', 'DeleteLeases','UpdateLeases',
+                    'AddLeases' 
                     ]
     # support for other names - this is experimental
     alias_calls = { 'get_authorities':'GetSites',
@@ -84,3 +87,4 @@ class PlShell:
             logger.debug('PlShell %s (%s) returned ... '%(name,actual_name))
             return result
         return func
+
index 2645ded..54cfaa1 100644 (file)
@@ -159,6 +159,25 @@ class PlSlices:
 
         return sfa_peer
 
+    def verify_slice_leases(self, slice, requested_leases, kept_leases, peer):
+        
+        leases = self.driver.shell.GetLeases({'name':slice['name']}, ['lease_id'])
+        current_leases = [lease['lease_id'] for lease in leases]
+        deleted_leases = list(set(current_leases).difference(kept_leases))
+
+        try:
+            if peer:
+                self.driver.shell.UnBindObjectFromPeer('slice', slice['slice_id'], peer['shortname'])
+            deleted=self.driver.shell.DeleteLeases(deleted_leases)
+            for lease in requested_leases:
+                added=self.driver.shell.AddLeases(lease['hostname'], slice['name'], int(lease['t_from']), int(lease['t_until']))
+
+        except: 
+            logger.log_exc('Failed to add/remove slice leases')
+
+        return leases
+
+
     def verify_slice_nodes(self, slice, requested_slivers, peer):
         
         nodes = self.driver.shell.GetNodes(slice['node_ids'], ['node_id', 'hostname', 'interface_ids'])
diff --git a/sfa/rspecs/elements/granularity.py b/sfa/rspecs/elements/granularity.py
new file mode 100644 (file)
index 0000000..16d30a0
--- /dev/null
@@ -0,0 +1,7 @@
+from sfa.rspecs.elements.element import Element
+
+class Granularity(Element):
+
+    fields = [
+        'grain',
+    ]
diff --git a/sfa/rspecs/elements/lease.py b/sfa/rspecs/elements/lease.py
new file mode 100644 (file)
index 0000000..d329a8c
--- /dev/null
@@ -0,0 +1,11 @@
+from sfa.rspecs.elements.element import Element
+class Lease(Element):
+    
+    fields = [
+        'lease_id',
+        'component_id',
+        'slice_id'
+        't_from',
+        't_until',    
+    ]
diff --git a/sfa/rspecs/elements/versions/sfav1Lease.py b/sfa/rspecs/elements/versions/sfav1Lease.py
new file mode 100644 (file)
index 0000000..7a2320e
--- /dev/null
@@ -0,0 +1,62 @@
+from sfa.util.sfalogging import logger
+from sfa.util.xml import XpathFilter
+from sfa.util.xrn import Xrn
+
+from sfa.rspecs.elements.element import Element
+from sfa.rspecs.elements.node import Node
+from sfa.rspecs.elements.sliver import Sliver
+from sfa.rspecs.elements.location import Location
+from sfa.rspecs.elements.hardware_type import HardwareType
+from sfa.rspecs.elements.disk_image import DiskImage
+from sfa.rspecs.elements.interface import Interface
+from sfa.rspecs.elements.bwlimit import BWlimit
+from sfa.rspecs.elements.pltag import PLTag
+from sfa.rspecs.elements.versions.sfav1Sliver import SFAv1Sliver
+from sfa.rspecs.elements.versions.sfav1PLTag import SFAv1PLTag
+from sfa.rspecs.elements.versions.pgv2Services import PGv2Services
+from sfa.rspecs.elements.lease import Lease
+
+from sfa.planetlab.plxrn import xrn_to_hostname
+
+class SFAv1Lease:
+
+    @staticmethod
+    def add_leases(xml, leases):
+        
+        network_elems = xml.xpath('//network')
+        if len(network_elems) > 0:
+            network_elem = network_elems[0]
+        elif len(leases) > 0:
+            network_urn = Xrn(leases[0]['component_id']).get_authority_urn().split(':')[0]
+            network_elem = xml.add_element('network', name = network_urn)
+        else:
+            network_elem = xml
+         
+        lease_elems = []       
+        for lease in leases:
+            lease_fields = ['lease_id', 'component_id', 'slice_id', 't_from', 't_until']
+            lease_elem = network_elem.add_instance('lease', lease, lease_fields)
+            lease_elems.append(lease_elem)
+
+
+    @staticmethod
+    def get_leases(xml, filter={}):
+        xpath = '//lease%s | //default:lease%s' % (XpathFilter.xpath(filter), XpathFilter.xpath(filter))
+        lease_elems = xml.xpath(xpath)
+        return SFAv1Lease.get_lease_objs(lease_elems)
+
+    @staticmethod
+    def get_lease_objs(lease_elems):
+        leases = []    
+        for lease_elem in lease_elems:
+            lease = Lease(lease_elem.attrib, lease_elem)
+            if lease.get('lease_id'):
+               lease['lease_id'] = lease_elem.attrib['lease_id']
+            lease['component_id'] = lease_elem.attrib['component_id']
+            lease['slice_id'] = lease_elem.attrib['slice_id']
+            lease['t_from'] = lease_elem.attrib['t_from']
+            lease['t_until'] = lease_elem.attrib['t_until']
+
+            leases.append(lease)
+        return leases            
+
index d8db263..024a52e 100644 (file)
@@ -57,6 +57,12 @@ class SFAv1Node:
             if location:
                 node_elem.add_instance('location', location, Location.fields)
 
+            # add granularity of the reservation system
+            granularity = node.get('granularity')
+            if granularity:
+                node_elem.add_instance('granularity', granularity, granularity.fields)
+
+
             if isinstance(node.get('interfaces'), list):
                 for interface in node.get('interfaces', []):
                     node_elem.add_instance('interface', interface, ['component_id', 'client_id', 'ipv4']) 
index ce3cac7..90f36a3 100644 (file)
@@ -20,6 +20,8 @@ RSpecElements = Enum(
     SERVICES='SERVICES',
     SLIVER='SLIVER', 
     SLIVER_TYPE='SLIVER_TYPE', 
+    LEASE='LEASE',
+    GRANULARITY='GRANULARITY',
 )
 
 class RSpecElement:
index 964da05..fd2e031 100644 (file)
@@ -8,6 +8,7 @@ from sfa.rspecs.elements.element import Element
 from sfa.rspecs.elements.versions.pgv2Link import PGv2Link
 from sfa.rspecs.elements.versions.sfav1Node import SFAv1Node
 from sfa.rspecs.elements.versions.sfav1Sliver import SFAv1Sliver
+from sfa.rspecs.elements.versions.sfav1Lease import SFAv1Lease
 
 class SFAv1(RSpecVersion):
     enabled = True
@@ -216,6 +217,14 @@ class SFAv1(RSpecVersion):
                 self.xml.append(network.element)
                 current_networks.append(current_network)
 
+    # Leases
+
+    def get_leases(self, filter=None):
+        return SFAv1Lease.get_leases(self.xml, filter)
+
+    def add_leases(self, leases, network = None, no_dupes=False):
+        SFAv1Lease.add_leases(self.xml, leases)
+
 if __name__ == '__main__':
     from sfa.rspecs.rspec import RSpec
     from sfa.rspecs.rspec_elements import *