fix allocation state names
[sfa.git] / sfa / planetlab / pldriver.py
index b73fcf7..bb6edbb 100644 (file)
@@ -1,13 +1,11 @@
-import time
 import datetime
 #
 from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \
 import datetime
 #
 from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \
-    RecordNotFound, SfaNotImplemented, SliverDoesNotExist
-
+    RecordNotFound, SfaNotImplemented, SliverDoesNotExist, SearchFailed
 from sfa.util.sfalogging import logger
 from sfa.util.defaultdict import defaultdict
 from sfa.util.sfatime import utcparse, datetime_to_string, datetime_to_epoch
 from sfa.util.sfalogging import logger
 from sfa.util.defaultdict import defaultdict
 from sfa.util.sfatime import utcparse, datetime_to_string, datetime_to_epoch
-from sfa.util.xrn import hrn_to_urn, get_leaf, urn_to_sliver_id
+from sfa.util.xrn import Xrn, hrn_to_urn, get_leaf
 from sfa.util.cache import Cache
 
 # one would think the driver should not need to mess with the SFA db, but..
 from sfa.util.cache import Cache
 
 # one would think the driver should not need to mess with the SFA db, but..
@@ -16,18 +14,16 @@ from sfa.storage.model import RegRecord
 
 # used to be used in get_ticket
 #from sfa.trust.sfaticket import SfaTicket
 
 # used to be used in get_ticket
 #from sfa.trust.sfaticket import SfaTicket
-
 from sfa.rspecs.version_manager import VersionManager
 from sfa.rspecs.rspec import RSpec
 
 # the driver interface, mostly provides default behaviours
 from sfa.managers.driver import Driver
 from sfa.rspecs.version_manager import VersionManager
 from sfa.rspecs.rspec import RSpec
 
 # the driver interface, mostly provides default behaviours
 from sfa.managers.driver import Driver
-
 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.plshell import PlShell
 import sfa.planetlab.peers as peers
 from sfa.planetlab.plaggregate import PlAggregate
 from sfa.planetlab.plslices import PlSlices
-from sfa.util.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, xrn_to_hostname
 
 
 def list_to_dict(recs, key):
 
 
 def list_to_dict(recs, key):
@@ -71,6 +67,9 @@ class PlDriver (Driver):
         if type == 'authority':
             sites = self.shell.GetSites([pl_record['login_base']])
             if not sites:
         if type == 'authority':
             sites = self.shell.GetSites([pl_record['login_base']])
             if not sites:
+                # xxx when a site gets registered through SFA we need to set its max_slices
+                if 'max_slices' not in pl_record:
+                    pl_record['max_slices']=2
                 pointer = self.shell.AddSite(pl_record)
             else:
                 pointer = sites[0]['site_id']
                 pointer = self.shell.AddSite(pl_record)
             else:
                 pointer = sites[0]['site_id']
@@ -157,6 +156,10 @@ class PlDriver (Driver):
                            'password', 'phone', 'url', 'bio', 'accepted_aup',
                            'enabled']:
                     update_fields[key] = all_fields[key]
                            'password', 'phone', 'url', 'bio', 'accepted_aup',
                            'enabled']:
                     update_fields[key] = all_fields[key]
+            # when updating a user, we always get a 'email' field at this point
+            # this is because 'email' is a native field in the RegUser object...
+            if 'email' in update_fields and not update_fields['email']:
+                del update_fields['email']
             self.shell.UpdatePerson(pointer, update_fields)
     
             if new_key:
             self.shell.UpdatePerson(pointer, update_fields)
     
             if new_key:
@@ -556,76 +559,24 @@ class PlDriver (Driver):
 
     def testbed_name (self): return "myplc"
 
 
     def testbed_name (self): return "myplc"
 
-    # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
     def aggregate_version (self):
     def aggregate_version (self):
-        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()) 
-        return {
-            'testbed':self.testbed_name(),
-            'geni_request_rspec_versions': request_rspec_versions,
-            'geni_ad_rspec_versions': ad_rspec_versions,
-            }
-
-    def list_slices (self, creds, options):
-        # look in cache first
-        if self.cache:
-            slices = self.cache.get('slices')
-            if slices:
-                logger.debug("PlDriver.list_slices returns from cache")
-                return slices
-    
-        # get data from db 
-        slices = self.shell.GetSlices({'peer_id': None}, ['name'])
-        slice_hrns = [slicename_to_hrn(self.hrn, slice['name']) for slice in slices]
-        slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns]
-    
-        # cache the result
-        if self.cache:
-            logger.debug ("PlDriver.list_slices stores value in cache")
-            self.cache.add('slices', slice_urns) 
-    
-        return slice_urns
-        
+        return {}
+
     # first 2 args are None in case of resource discovery
     # first 2 args are None in case of resource discovery
-    def list_resources (self, slice_urn, slice_hrn, creds, options):
-        cached_requested = options.get('cached', True) 
-    
-        version_manager = VersionManager()
-        # get the rspec's return format from options
-        rspec_version = version_manager.get_version(options.get('geni_rspec_version'))
-        version_string = "rspec_%s" % (rspec_version)
-    
-        #panos adding the info option to the caching key (can be improved)
-        if options.get('info'):
-            version_string = version_string + "_"+options.get('info', 'default')
-    
-        # look in cache first
-        if cached_requested and self.cache and not slice_hrn:
-            rspec = self.cache.get(version_string)
-            if rspec:
-                logger.debug("PlDriver.ListResources: returning cached advertisement")
-                return rspec 
-    
-        #panos: passing user-defined options
-        #print "manager options = ",options
+    def list_resources (self, version=None, options={}):
         aggregate = PlAggregate(self)
         aggregate = PlAggregate(self)
-        rspec =  aggregate.get_rspec(slice_xrn=slice_urn, version=rspec_version, 
-                                     options=options)
-    
-        # cache the result
-        if self.cache and not slice_hrn:
-            logger.debug("PlDriver.ListResources: stores advertisement in cache")
-            self.cache.add(version_string, rspec)
-    
+        rspec =  aggregate.list_resources(version=version, options=options)
         return rspec
         return rspec
+
+    def describe(self, urns, version, options={}):
+        aggregate = PlAggregate(self)
+        return aggregate.describe(urns, version=version, options=options)
     
     
-    def sliver_status (self, slice_urn, slice_hrn):
+    def status (self, urns, options={}):
+        aggregate = PlAggregate(self)
+        desc =  aggregate.describe(urns)
+        return desc['geni_slivers']
+
         # find out where this slice is currently running
         slicename = hrn_to_pl_slicename(slice_hrn)
         
         # find out where this slice is currently running
         slicename = hrn_to_pl_slicename(slice_hrn)
         
@@ -647,7 +598,7 @@ class PlDriver (Driver):
             persons = self.shell.GetPersons(slice['person_ids'], ['key_ids'])
             key_ids = [key_id for person in persons for key_id in person['key_ids']]
             person_keys = self.shell.GetKeys(key_ids)
             persons = self.shell.GetPersons(slice['person_ids'], ['key_ids'])
             key_ids = [key_id for person in persons for key_id in person['key_ids']]
             person_keys = self.shell.GetKeys(key_ids)
-            keys = [key['key'] for key in keys]
+            keys = [key['key'] for key in person_keys]
 
             user.update({'urn': slice_urn,
                          'login': slice['name'],
 
             user.update({'urn': slice_urn,
                          'login': slice['name'],
@@ -658,9 +609,6 @@ class PlDriver (Driver):
         site_ids = [node['site_id'] for node in nodes]
     
         result = {}
         site_ids = [node['site_id'] for node in nodes]
     
         result = {}
-        top_level_status = 'unknown'
-        if nodes:
-            top_level_status = 'ready'
         result['geni_urn'] = slice_urn
         result['pl_login'] = slice['name']
         result['pl_expires'] = datetime_to_string(utcparse(slice['expires']))
         result['geni_urn'] = slice_urn
         result['pl_login'] = slice['name']
         result['pl_expires'] = datetime_to_string(utcparse(slice['expires']))
@@ -676,30 +624,31 @@ class PlDriver (Driver):
             if node['last_contact'] is not None:
                 
                 res['pl_last_contact'] = datetime_to_string(utcparse(node['last_contact']))
             if node['last_contact'] is not None:
                 
                 res['pl_last_contact'] = datetime_to_string(utcparse(node['last_contact']))
-            sliver_id = urn_to_sliver_id(slice_urn, slice['slice_id'], node['node_id'], authority=self.hrn) 
-            res['geni_urn'] = sliver_id
+            sliver_id = "%s:%s" % (slice['slice_id'], node['node_id'])
+            sliver_xrn = Xrn(slice_urn, id = sliver_id)
+            sliver_xrn.set_authority(self.hrn)
+            res['geni_urn'] = sliver_xrn.get_urn()
             if node['boot_state'] == 'boot':
                 res['geni_status'] = 'ready'
             else:
                 res['geni_status'] = 'failed'
             if node['boot_state'] == 'boot':
                 res['geni_status'] = 'ready'
             else:
                 res['geni_status'] = 'failed'
-                top_level_status = 'failed' 
+            res['geni_allocation_status'] = 'geni_provisioned'
                 
             res['geni_error'] = ''
             res['users'] = [user]  
                 
             res['geni_error'] = ''
             res['users'] = [user]  
-    
             resources.append(res)
             
             resources.append(res)
             
-        result['geni_status'] = top_level_status
         result['geni_resources'] = resources
         return result
 
         result['geni_resources'] = resources
         return result
 
-    def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, users, options):
-
+    def allocate (self, urn, rspec_string, options={}):
+        xrn = Xrn(urn)
         aggregate = PlAggregate(self)
         slices = PlSlices(self)
         aggregate = PlAggregate(self)
         slices = PlSlices(self)
-        peer = slices.get_peer(slice_hrn)
-        sfa_peer = slices.get_sfa_peer(slice_hrn)
+        peer = slices.get_peer(xrn.get_hrn())
+        sfa_peer = slices.get_sfa_peer(xrn.get_hrn())
         slice_record=None    
         slice_record=None    
+        users = options.get('geni_users', [])
         if users:
             slice_record = users[0].get('slice_record', {})
     
         if users:
             slice_record = users[0].get('slice_record', {})
     
@@ -708,11 +657,11 @@ class PlDriver (Driver):
         requested_attributes = rspec.version.get_slice_attributes()
         
         # ensure site record exists
         requested_attributes = rspec.version.get_slice_attributes()
         
         # ensure site record exists
-        site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer, options=options)
+        site = slices.verify_site(xrn.hrn, slice_record, peer, sfa_peer, options=options)
         # ensure slice record exists
         # ensure slice record exists
-        slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer, options=options)
+        slice = slices.verify_slice(xrn.hrn, slice_record, peer, sfa_peer, options=options)
         # ensure person records exists
         # ensure person records exists
-        persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer, options=options)
+        persons = slices.verify_persons(xrn.hrn, slice, users, peer, sfa_peer, options=options)
         # ensure slice attributes exists
         slices.verify_slice_attributes(slice, requested_attributes, options=options)
         
         # ensure slice attributes exists
         slices.verify_slice_attributes(slice, requested_attributes, options=options)
         
@@ -721,29 +670,58 @@ class PlDriver (Driver):
         for node in rspec.version.get_nodes_with_slivers():
             hostname = None
             if node.get('component_name'):
         for node in rspec.version.get_nodes_with_slivers():
             hostname = None
             if node.get('component_name'):
-                hostname = node.get('component_name')
+                hostname = node.get('component_name').strip()
             elif node.get('component_id'):
             elif node.get('component_id'):
-                hostname = xrn_to_hostname(node.get('component_id'))
+                hostname = xrn_to_hostname(node.get('component_id').strip())
             if hostname:
                 requested_slivers.append(hostname)
         nodes = slices.verify_slice_nodes(slice, requested_slivers, peer) 
    
         # add/remove links links 
         slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
             if hostname:
                 requested_slivers.append(hostname)
         nodes = slices.verify_slice_nodes(slice, requested_slivers, peer) 
    
         # 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['start_time'] = lease.get('start_time')
+               requested_lease['duration'] = lease.get('duration')
+            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.
         slices.handle_peer(site, slice, persons, peer)
         
     
         # handle MyPLC peer association.
         # only used by plc and ple.
         slices.handle_peer(site, slice, persons, peer)
         
-        return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
-
-    def delete_sliver (self, slice_urn, slice_hrn, creds, options):
-        slicename = hrn_to_pl_slicename(slice_hrn)
-        slices = self.shell.GetSlices({'name': slicename})
+        return aggregate.describe([xrn.get_urn()], version=rspec.version, allocation_status='geni_allocated')
+
+    def provision(self, urns, options={}):
+        return self.describe(urns, None, options=options, allocation_status='geni_provisioned')
+
+    def delete(self, urns, options={}):
+        names = []
+        ids = []
+        for urn in urns:
+            xrn = PlXrn(xrn=urn, type='slice')
+            names.append(xrn.pl_slicename())
+            if xrn.id:
+                ids.append(xrn.id)    
+        slices = self.shell.GetSlices({'name': names})
         if not slices:
         if not slices:
-            return 1
+            raise SearchFailed(urns)
         slice = slices[0]
         slice = slices[0]
-    
+        if ids:
+            node_ids = ids
+        else:
+            node_ids = slice['node_ids']
+        slice_hrn = PlXrn(auth=self.hrn, slicename=slice['name']).get_hrn()     
         # determine if this is a peer slice
         # xxx I wonder if this would not need to use PlSlices.get_peer instead 
         # in which case plc.peers could be deprecated as this here
         # determine if this is a peer slice
         # xxx I wonder if this would not need to use PlSlices.get_peer instead 
         # in which case plc.peers could be deprecated as this here
@@ -752,17 +730,22 @@ class PlDriver (Driver):
         try:
             if peer:
                 self.shell.UnBindObjectFromPeer('slice', slice['slice_id'], peer)
         try:
             if peer:
                 self.shell.UnBindObjectFromPeer('slice', slice['slice_id'], peer)
-            self.shell.DeleteSliceFromNodes(slicename, slice['node_ids'])
+            self.shell.DeleteSliceFromNodes(slice['slice_id'], node_ids)
         finally:
             if peer:
                 self.shell.BindObjectToPeer('slice', slice['slice_id'], peer, slice['peer_slice_id'])
         finally:
             if peer:
                 self.shell.BindObjectToPeer('slice', slice['slice_id'], peer, slice['peer_slice_id'])
-        return 1
+        return True
     
     
-    def renew_sliver (self, slice_urn, slice_hrn, creds, expiration_time, options):
-        slicename = hrn_to_pl_slicename(slice_hrn)
-        slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
+    def renew (self, urns, expiration_time, options={}):
+        # we can only renew slices, not individual slivers. ignore sliver
+        # ids in the urn 
+        names = []
+        for urn in urns:
+            xrn = PlXrn(xrn=urn, type='slice')
+            names.append(xrn.pl_slicename())
+        slices = self.shell.GetSlices(names, ['slice_id'])
         if not slices:
         if not slices:
-            raise RecordNotFound(slice_hrn)
+            raise SearchFailed(urns)
         slice = slices[0]
         requested_time = utcparse(expiration_time)
         record = {'expires': int(datetime_to_epoch(requested_time))}
         slice = slices[0]
         requested_time = utcparse(expiration_time)
         record = {'expires': int(datetime_to_epoch(requested_time))}
@@ -772,22 +755,21 @@ class PlDriver (Driver):
         except:
             return False
 
         except:
             return False
 
-    # remove the 'enabled' tag 
-    def start_slice (self, slice_urn, slice_hrn, creds):
-        slicename = hrn_to_pl_slicename(slice_hrn)
-        slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
-        if not slices:
-            raise RecordNotFound(slice_hrn)
-        slice_id = slices[0]['slice_id']
-        slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'}, ['slice_tag_id'])
-        # just remove the tag if it exists
-        if slice_tags:
-            self.shell.DeleteSliceTag(slice_tags[0]['slice_tag_id'])
-        return 1
+    def perform_operational_action (self, urns, action, options={}):
+        # MyPLC doesn't support operational actions. Lets pretend like it
+        # supports start, but reject everything else.
+        action = action.lower()
+        if action == 'geni_start':
+            pass
+        else:
+            raise UnsupportedOperation(action)
+        description = self.describe(urns, None, options)
+        return description['geni_slivers']
 
     # set the 'enabled' tag to 0
 
     # set the 'enabled' tag to 0
-    def stop_slice (self, slice_urn, slice_hrn, creds):
-        slicename = hrn_to_pl_slicename(slice_hrn)
+    def shutdown (self, xrn, options={}):
+        xrn = PlXrn(xrn=xrn, type='slice')
+        slicename = xrn.pl_slicename()
         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
         if not slices:
             raise RecordNotFound(slice_hrn)
         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
         if not slices:
             raise RecordNotFound(slice_hrn)
@@ -799,76 +781,3 @@ class PlDriver (Driver):
             tag_id = slice_tags[0]['slice_tag_id']
             self.shell.UpdateSliceTag(tag_id, '0')
         return 1
             tag_id = slice_tags[0]['slice_tag_id']
             self.shell.UpdateSliceTag(tag_id, '0')
         return 1
-    
-    def reset_slice (self, slice_urn, slice_hrn, creds):
-        raise SfaNotImplemented ("reset_slice not available at this interface")
-    
-    # xxx this code is quite old and has not run for ages
-    # it is obviously totally broken and needs a rewrite
-    def get_ticket (self, slice_urn, slice_hrn, creds, rspec_string, options):
-        raise SfaNotImplemented,"PlDriver.get_ticket needs a rewrite"
-# please keep this code for future reference
-#        slices = PlSlices(self)
-#        peer = slices.get_peer(slice_hrn)
-#        sfa_peer = slices.get_sfa_peer(slice_hrn)
-#    
-#        # get the slice record
-#        credential = api.getCredential()
-#        interface = api.registries[api.hrn]
-#        registry = api.server_proxy(interface, credential)
-#        records = registry.Resolve(xrn, credential)
-#    
-#        # make sure we get a local slice record
-#        record = None
-#        for tmp_record in records:
-#            if tmp_record['type'] == 'slice' and \
-#               not tmp_record['peer_authority']:
-#    #Error (E0602, GetTicket): Undefined variable 'SliceRecord'
-#                slice_record = SliceRecord(dict=tmp_record)
-#        if not record:
-#            raise RecordNotFound(slice_hrn)
-#        
-#        # similar to CreateSliver, we must verify that the required records exist
-#        # at this aggregate before we can issue a ticket
-#        # parse rspec
-#        rspec = RSpec(rspec_string)
-#        requested_attributes = rspec.version.get_slice_attributes()
-#    
-#        # ensure site record exists
-#        site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer)
-#        # ensure slice record exists
-#        slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer)
-#        # ensure person records exists
-#    # xxx users is undefined in this context
-#        persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer)
-#        # ensure slice attributes exists
-#        slices.verify_slice_attributes(slice, requested_attributes)
-#        
-#        # get sliver info
-#        slivers = slices.get_slivers(slice_hrn)
-#    
-#        if not slivers:
-#            raise SliverDoesNotExist(slice_hrn)
-#    
-#        # get initscripts
-#        initscripts = []
-#        data = {
-#            'timestamp': int(time.time()),
-#            'initscripts': initscripts,
-#            'slivers': slivers
-#        }
-#    
-#        # create the ticket
-#        object_gid = record.get_gid_object()
-#        new_ticket = SfaTicket(subject = object_gid.get_subject())
-#        new_ticket.set_gid_caller(api.auth.client_gid)
-#        new_ticket.set_gid_object(object_gid)
-#        new_ticket.set_issuer(key=api.key, subject=self.hrn)
-#        new_ticket.set_pubkey(object_gid.get_pubkey())
-#        new_ticket.set_attributes(data)
-#        new_ticket.set_rspec(rspec)
-#        #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
-#        new_ticket.encode()
-#        new_ticket.sign()
-#    
-#        return new_ticket.save_to_string(save_parents=True)