review management of relationships - allow to update them with sfaadmin
[sfa.git] / sfa / plc / pldriver.py
index fa97183..49908ba 100644 (file)
@@ -6,12 +6,13 @@ from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \
 
 from sfa.util.sfalogging import logger
 from sfa.util.defaultdict import defaultdict
-from sfa.util.sfatime import utcparse
+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.cache import Cache
 
 # one would think the driver should not need to mess with the SFA db, but..
-from sfa.storage.table import SfaTable
+from sfa.storage.alchemy import dbsession
+from sfa.storage.model import RegRecord
 
 # used to be used in get_ticket
 #from sfa.trust.sfaticket import SfaTicket
@@ -26,7 +27,7 @@ from sfa.plc.plshell import PlShell
 import sfa.plc.peers as peers
 from sfa.plc.plaggregate import PlAggregate
 from sfa.plc.plslices import PlSlices
-from sfa.util.plxrn import slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_login_base
+from sfa.util.plxrn import PlXrn, slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename
 
 
 def list_to_dict(recs, key):
@@ -59,14 +60,6 @@ class PlDriver (Driver):
     ########## registry oriented
     ########################################
 
-    ########## disabled users 
-    def is_enabled (self, record):
-        # the incoming record was augmented already, so 'enabled' should be set
-        if record['type'] == 'user':
-            return record['enabled']
-        # only users can be disabled
-        return True
-
     def augment_records_with_testbed_info (self, sfa_records):
         return self.fill_record_info (sfa_records)
 
@@ -94,8 +87,10 @@ class PlDriver (Driver):
                  pointer = slices[0]['slice_id']
 
         elif type == 'user':
-            persons = self.shell.GetPersons([sfa_record['email']])
+            persons = self.shell.GetPersons({'email':sfa_record['email']})
             if not persons:
+                for key in ['first_name','last_name']:
+                    if key not in sfa_record: sfa_record[key]='*from*sfa*'
                 pointer = self.shell.AddPerson(dict(sfa_record))
             else:
                 pointer = persons[0]['person_id']
@@ -109,13 +104,21 @@ class PlDriver (Driver):
                 self.shell.AddPersonToSite(pointer, login_base)
     
             # What roles should this user have?
-            self.shell.AddRoleToPerson('user', pointer)
+            roles=[]
+            if 'roles' in sfa_record: 
+                # if specified in xml, but only low-level roles
+                roles = [ role for role in sfa_record['roles'] if role in ['user','tech'] ]
+            # at least user if no other cluse could be found
+            if not roles:
+                roles=['user']
+            for role in roles:
+                self.shell.AddRoleToPerson(role, pointer)
             # Add the user's key
             if pub_key:
                 self.shell.AddPersonKey(pointer, {'key_type' : 'ssh', 'key' : pub_key})
 
         elif type == 'node':
-            login_base = hrn_to_pl_login_base(sfa_record['authority'])
+            login_base = PlXrn(xrn=sfa_record['authority'],type='node').pl_login_base()
             nodes = self.shell.GetNodes([pl_record['hostname']])
             if not nodes:
                 pointer = self.shell.AddNode(login_base, pl_record)
@@ -206,7 +209,7 @@ class PlDriver (Driver):
 
 
     ##
-    # Convert SFA fields to PLC fields for use when registering up updating
+    # Convert SFA fields to PLC fields for use when registering or updating
     # registry record in the PLC database
     #
 
@@ -224,8 +227,10 @@ class PlDriver (Driver):
                pl_record["url"] = sfa_record["url"]
            if "description" in sfa_record:
                pl_record["description"] = sfa_record["description"]
-           if "expires" in sfa_record:
-               pl_record["expires"] = int(sfa_record["expires"])
+            if "expires" in sfa_record:
+                date = utcparse(sfa_record['expires'])
+                expires = datetime_to_epoch(date)
+                pl_record["expires"] = expires
 
         elif type == "node":
             if not "hostname" in pl_record:
@@ -239,7 +244,7 @@ class PlDriver (Driver):
                 pl_record["model"] = "geni"
 
         elif type == "authority":
-            pl_record["login_base"] = hrn_to_pl_login_base(hrn)
+            pl_record["login_base"] = PlXrn(xrn=hrn,type='authority').pl_login_base()
             if "name" not in sfa_record:
                 pl_record["name"] = hrn
             if "abbreviated_name" not in sfa_record:
@@ -400,6 +405,11 @@ class PlDriver (Driver):
                                if site_id in sites]
                 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
                 record['sites'] = site_hrns
+
+            if 'expires' in record:
+                date = utcparse(record['expires'])
+                datestring = datetime_to_string(date)
+                record['expires'] = datestring 
             
         return records   
 
@@ -439,16 +449,15 @@ class PlDriver (Driver):
         # we'll replace pl ids (person_ids) with hrns from the sfa records
         # we obtain
         
-        # get the sfa records
-        table = SfaTable()
+        # get the registry records
         person_list, persons = [], {}
-        person_list = table.find({'type': 'user', 'pointer': person_ids})
+        person_list = dbsession.query (RegRecord).filter(RegRecord.pointer.in_(person_ids))
         # create a hrns keyed on the sfa record's pointer.
         # Its possible for multiple records to have the same pointer so
         # the dict's value will be a list of hrns.
         persons = defaultdict(list)
         for person in person_list:
-            persons[person['pointer']].append(person)
+            persons[person.pointer].append(person)
 
         # get the pl records
         pl_person_list, pl_persons = [], {}
@@ -462,13 +471,14 @@ class PlDriver (Driver):
             #    continue 
             sfa_info = {}
             type = record['type']
+            logger.info("fill_record_sfa_info - incoming record typed %s"%type)
             if (type == "slice"):
                 # all slice users are researchers
                 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
                 record['PI'] = []
                 record['researcher'] = []
                 for person_id in record.get('person_ids', []):
-                    hrns = [person['hrn'] for person in persons[person_id]]
+                    hrns = [person.hrn for person in persons[person_id]]
                     record['researcher'].extend(hrns)                
 
                 # pis at the slice's site
@@ -476,12 +486,13 @@ class PlDriver (Driver):
                     pl_pis = site_pis[record['site_id']]
                     pi_ids = [pi['person_id'] for pi in pl_pis]
                     for person_id in pi_ids:
-                        hrns = [person['hrn'] for person in persons[person_id]]
+                        hrns = [person.hrn for person in persons[person_id]]
                         record['PI'].extend(hrns)
                         record['geni_creator'] = record['PI'] 
                 
             elif (type.startswith("authority")):
                 record['url'] = None
+                logger.info("fill_record_sfa_info - authority xherex")
                 if record['pointer'] != -1:
                     record['PI'] = []
                     record['operator'] = []
@@ -490,7 +501,7 @@ class PlDriver (Driver):
                         if pointer not in persons or pointer not in pl_persons:
                             # this means there is not sfa or pl record for this user
                             continue   
-                        hrns = [person['hrn'] for person in persons[pointer]] 
+                        hrns = [person.hrn for person in persons[pointer]] 
                         roles = pl_persons[pointer]['roles']   
                         if 'pi' in roles:
                             record['PI'].extend(hrns)
@@ -504,6 +515,7 @@ class PlDriver (Driver):
                 # xxx TODO: URI, LatLong, IP, DNS
     
             elif (type == "user"):
+                logger.info('setting user.email')
                 sfa_info['email'] = record.get("email", "")
                 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
                 sfa_info['geni_certificate'] = record['gid'] 
@@ -513,9 +525,9 @@ class PlDriver (Driver):
 
     ####################
     # plcapi works by changes, compute what needs to be added/deleted
-    def update_relation (self, subject_type, target_type, subject_id, target_ids):
+    def update_relation (self, subject_type, target_type, relation_name, subject_id, target_ids):
         # hard-wire the code for slice/user for now, could be smarter if needed
-        if subject_type =='slice' and target_type == 'user':
+        if subject_type =='slice' and target_type == 'user' and relation_name == 'researcher':
             subject=self.shell.GetSlices (subject_id)[0]
             current_target_ids = subject['person_ids']
             add_target_ids = list ( set (target_ids).difference(current_target_ids))
@@ -527,8 +539,15 @@ class PlDriver (Driver):
             for target_id in del_target_ids:
                 logger.debug ("del_target_id = %s (type=%s)"%(target_id,type(target_id)))
                 self.shell.DeletePersonFromSlice (target_id, subject_id)
+        elif subject_type == 'authority' and target_type == 'user' and relation_name == 'pi':
+            # due to the plcapi limitations this means essentially adding pi role to all people in the list
+            # it's tricky to remove any pi role here, although it might be desirable
+            persons = self.shell.GetPersons (target_ids)
+            for person in persons: 
+                if 'pi' not in person['roles']:
+                    self.shell.AddRoleToPerson('pi',person['person_id'])
         else:
-            logger.info('unexpected relation to maintain, %s -> %s'%(subject_type,target_type))
+            logger.info('unexpected relation %s to maintain, %s -> %s'%(relation_name,subject_type,target_type))
 
         
     ########################################
@@ -618,6 +637,24 @@ class PlDriver (Driver):
         # report about the local nodes only
         nodes = self.shell.GetNodes({'node_id':slice['node_ids'],'peer_id':None},
                               ['node_id', 'hostname', 'site_id', 'boot_state', 'last_contact'])
+
+        if len(nodes) == 0:
+            raise SliverDoesNotExist("You have not allocated any slivers here") 
+
+        # get login info
+        user = {}
+        if slice['person_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]
+
+            user.update({'urn': slice_urn,
+                         'login': slice['name'],
+                         'protocol': ['ssh'],
+                         'port': ['22'],
+                         'keys': keys})
+
         site_ids = [node['site_id'] for node in nodes]
     
         result = {}
@@ -626,7 +663,8 @@ class PlDriver (Driver):
             top_level_status = 'ready'
         result['geni_urn'] = slice_urn
         result['pl_login'] = slice['name']
-        result['pl_expires'] = datetime.datetime.fromtimestamp(slice['expires']).ctime()
+        result['pl_expires'] = datetime_to_string(utcparse(slice['expires']))
+        result['geni_expires'] = datetime_to_string(utcparse(slice['expires']))
         
         resources = []
         for node in nodes:
@@ -634,9 +672,11 @@ class PlDriver (Driver):
             res['pl_hostname'] = node['hostname']
             res['pl_boot_state'] = node['boot_state']
             res['pl_last_contact'] = node['last_contact']
+            res['geni_expires'] = datetime_to_string(utcparse(slice['expires']))
             if node['last_contact'] is not None:
-                res['pl_last_contact'] = datetime.datetime.fromtimestamp(node['last_contact']).ctime()
-            sliver_id = urn_to_sliver_id(slice_urn, slice['slice_id'], node['node_id']) 
+                
+                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
             if node['boot_state'] == 'boot':
                 res['geni_status'] = 'ready'
@@ -645,6 +685,7 @@ class PlDriver (Driver):
                 top_level_status = 'failed' 
                 
             res['geni_error'] = ''
+            res['users'] = [user]  
     
             resources.append(res)
             
@@ -676,7 +717,15 @@ class PlDriver (Driver):
         slices.verify_slice_attributes(slice, requested_attributes, options=options)
         
         # add/remove slice from nodes
-        requested_slivers = [node.get('component_name') for node in rspec.version.get_nodes_with_slivers()]
+        requested_slivers = []
+        for node in rspec.version.get_nodes_with_slivers():
+            hostname = None
+            if node.get('component_name'):
+                hostname = node.get('component_name')
+            elif node.get('component_id'):
+                hostname = xrn_to_hostname(node.get('component_id'))
+            if hostname:
+                requested_slivers.append(hostname)
         nodes = slices.verify_slice_nodes(slice, requested_slivers, peer) 
    
         # add/remove links links 
@@ -716,7 +765,7 @@ class PlDriver (Driver):
             raise RecordNotFound(slice_hrn)
         slice = slices[0]
         requested_time = utcparse(expiration_time)
-        record = {'expires': int(time.mktime(requested_time.timetuple()))}
+        record = {'expires': int(datetime_to_epoch(requested_time))}
         try:
             self.shell.UpdateSlice(slice['slice_id'], record)
             return True