dealing with reg-researchers vs researcher
[sfa.git] / sfa / managers / registry_manager.py
index f55e69b..bfff502 100644 (file)
@@ -12,6 +12,8 @@ from sfa.util.xrn import Xrn, get_authority, hrn_to_urn, urn_to_hrn
 from sfa.util.version import version_core
 from sfa.util.sfalogging import logger
 
+from sfa.util.printable import printable
+
 from sfa.trust.gid import GID 
 from sfa.trust.credential import Credential
 from sfa.trust.certificate import Certificate, Keypair, convert_public_key
@@ -23,15 +25,53 @@ from sfa.storage.model import make_record, RegRecord, RegAuthority, RegUser, Reg
 # them on the xmlrpc wire
 from sqlalchemy.orm.collections import InstrumentedList
 
+### historical note -- april 2014
+# the myslice chaps rightfully complained about the following discrepency
+# they found that
+# * read operations (resolve) expose stuff like e.g. 'reg-researchers', but that
+# * write operations (register, update) need e.g. 'researcher' to be set - it ignores 'reg-researchers'
+#
+# the 'normalize' helper functions below aim at ironing this out
+# however in order to break as few as possible we essentially make sure that *both* fields are set
+# upon entering the write methods (so again register and update) because some driver code
+# might depend on the presence of, say, 'researcher'
+
+# registry calls this 'reg-researchers'
+# some drivers call this 'researcher'
+# this is even more confusing as people might use 'researchers'
+def normalize_input_researcher (record):
+    # this aims at detecting a mispelled input
+    if 'researchers' in record and 'researcher' not in record:
+        record['researcher']=record['researchers']
+        del record['researchers']
+    # this looks right, use this for both keys
+    if 'reg-researchers' in record:
+        # and issue a warning if they were both set as we're overwriting some user data here
+        if 'researcher' in record:
+            logger.warning ("normalize_input_researcher: incoming record has both values, using reg-researchers")
+        record['researcher']=record['reg-researchers']
+    # we only have one key set, duplicate for the other one
+    elif 'researcher' in record:
+        logger.warning ("normalize_input_researcher: you should use 'reg-researchers' instead ot 'researcher'")
+        record['reg-researchers']=record['researcher']
+    # if at this point we still have 'researchers' it's going to be ignored and that might be confusing
+    if 'researchers' in record:
+        logger.warning ("normalize_input_researcher: incoming record has confusing 'researchers' key - ignored - use 'reg-researchers' instead")
+
+def normalize_input_record (record):
+    normalize_input_researcher (record)
+    return record
+
 class RegistryManager:
 
-    def __init__ (self, config): pass
+    def __init__ (self, config): 
+        logger.info("Creating RegistryManager[%s]"%id(self))
 
     # The GENI GetVersion call
     def GetVersion(self, api, options):
         peers = dict ( [ (hrn,interface.get_url()) for (hrn,interface) in api.registries.iteritems() 
                        if hrn != api.hrn])
-        xrn=Xrn(api.hrn)
+        xrn=Xrn(api.hrn,type='authority')
         return version_core({'interface':'registry',
                              'sfa': 2,
                              'geni_api': 2,
@@ -39,18 +79,19 @@ class RegistryManager:
                              'urn':xrn.get_urn(),
                              'peers':peers})
     
-    def GetCredential(self, api, xrn, type, caller_xrn=None):
-        dbsession = api.dbsession()
+    def GetCredential(self, api, xrn, input_type, caller_xrn=None):
         # convert xrn to hrn     
-        if type:
-            hrn = urn_to_hrn(xrn)[0]
+        if input_type:
+            hrn, _ = urn_to_hrn(xrn)
+            type = input_type
         else:
             hrn, type = urn_to_hrn(xrn)
 
         # Slivers don't have credentials but users should be able to 
         # specify a sliver xrn and receive the slice's credential
-        if type == 'sliver' or '-' in Xrn(hrn).leaf:
-            slice_xrn = self.driver.sliver_to_slice_xrn(hrn)
+        # However if input_type is specified
+        if type == 'sliver' or ( not input_type and '-' in Xrn(hrn).leaf):
+            slice_xrn = api.driver.sliver_to_slice_xrn(hrn)
             hrn = slice_xrn.hrn 
   
         # Is this a root or sub authority
@@ -58,7 +99,9 @@ class RegistryManager:
         if not auth_hrn or hrn == api.config.SFA_INTERFACE_HRN:
             auth_hrn = hrn
         auth_info = api.auth.get_auth_info(auth_hrn)
+
         # get record info
+        dbsession = api.dbsession()
         record=dbsession.query(RegRecord).filter_by(type=type,hrn=hrn).first()
         if not record:
             raise RecordNotFound("hrn=%s, type=%s"%(hrn,type))
@@ -173,7 +216,7 @@ class RegistryManager:
         if details:
             # in details mode we get as much info as we can, which involves contacting the 
             # testbed for getting implementation details about the record
-            self.driver.augment_records_with_testbed_info(local_dicts)
+            api.driver.augment_records_with_testbed_info(local_dicts)
             # also we fill the 'url' field for known authorities
             # used to be in the driver code, sounds like a poorman thing though
             def solve_neighbour_url (record):
@@ -265,18 +308,19 @@ class RegistryManager:
     # subject_record describes the subject of the relationships
     # ref_record contains the target values for the various relationships we need to manage
     # (to begin with, this is just the slice x person (researcher) and authority x person (pi) relationships)
-    def update_driver_relations (self, subject_obj, ref_obj, dbsession):
+    def update_driver_relations (self, api, subject_obj, ref_obj):
         type=subject_obj.type
         #for (k,v) in subject_obj.__dict__.items(): print k,'=',v
         if type=='slice' and hasattr(ref_obj,'researcher'):
-            self.update_driver_relation(subject_obj, ref_obj.researcher, 'user', 'researcher', dbsession)
+            self.update_driver_relation(api, subject_obj, ref_obj.researcher, 'user', 'researcher')
         elif type=='authority' and hasattr(ref_obj,'pi'):
-            self.update_driver_relation(subject_obj,ref_obj.pi, 'user', 'pi', dbsession)
+            self.update_driver_relation(api, subject_obj,ref_obj.pi, 'user', 'pi')
         
     # field_key is the name of one field in the record, typically 'researcher' for a 'slice' record
     # hrns is the list of hrns that should be linked to the subject from now on
     # target_type would be e.g. 'user' in the 'slice' x 'researcher' example
-    def update_driver_relation (self, record_obj, hrns, target_type, relation_name, dbsession):
+    def update_driver_relation (self, api, record_obj, hrns, target_type, relation_name):
+        dbsession=api.dbsession()
         # locate the linked objects in our db
         subject_type=record_obj.type
         subject_id=record_obj.pointer
@@ -284,10 +328,14 @@ class RegistryManager:
         link_id_tuples = dbsession.query(RegRecord.pointer).filter_by(type=target_type).filter(RegRecord.hrn.in_(hrns)).all()
         # sqlalchemy returns named tuples for columns
         link_ids = [ tuple.pointer for tuple in link_id_tuples ]
-        self.driver.update_relation (subject_type, target_type, relation_name, subject_id, link_ids)
+        api.driver.update_relation (subject_type, target_type, relation_name, subject_id, link_ids)
 
     def Register(self, api, record_dict):
     
+        logger.debug("Register: entering with record_dict=%s"%printable(record_dict))
+        normalize_input_record (record_dict)
+        logger.debug("Register: normalized record_dict=%s"%printable(record_dict))
+
         dbsession=api.dbsession()
         hrn, type = record_dict['hrn'], record_dict['type']
         urn = hrn_to_urn(hrn,type)
@@ -337,7 +385,7 @@ class RegistryManager:
             if pi_hrns is not None: record.update_pis (pi_hrns, dbsession)
 
         elif isinstance (record, RegSlice):
-            researcher_hrns = getattr(record,'researcher',None)
+            researcher_hrns = getattr(record,'reg-researchers',None)
             if researcher_hrns is not None: record.update_researchers (researcher_hrns, dbsession)
         
         elif isinstance (record, RegUser):
@@ -347,18 +395,23 @@ class RegistryManager:
                 record.reg_keys = [ RegKey (key) for key in record.keys ]
             
         # update testbed-specific data if needed
-        pointer = self.driver.register (record.__dict__, hrn, pub_key)
+        pointer = api.driver.register (record.__dict__, hrn, pub_key)
 
         record.pointer=pointer
         dbsession.add(record)
         dbsession.commit()
     
         # update membership for researchers, pis, owners, operators
-        self.update_driver_relations (record, record, dbsession)
+        self.update_driver_relations (api, record, record)
         
         return record.get_gid_object().save_to_string(save_parents=True)
     
     def Update(self, api, record_dict):
+
+        logger.debug("Update: entering with record_dict=%s"%printable(record_dict))
+        normalize_input_record (record_dict)
+        logger.debug("Update: normalized record_dict=%s"%printable(record_dict))
+
         dbsession=api.dbsession()
         assert ('type' in record_dict)
         new_record=make_record(dict=record_dict)
@@ -397,7 +450,7 @@ class RegistryManager:
 
         # update native relations
         if isinstance (record, RegSlice):
-            researcher_hrns = getattr(new_record,'researcher',None)
+            researcher_hrns = getattr(new_record,'reg-researchers',None)
             if researcher_hrns is not None: record.update_researchers (researcher_hrns, dbsession)
 
         elif isinstance (record, RegAuthority):
@@ -412,7 +465,7 @@ class RegistryManager:
         print "DO NOT REMOVE ME before driver.update, record=%s"%record
         new_key_pointer = -1
         try:
-           (pointer, new_key_pointer) = self.driver.update (record.__dict__, new_record.__dict__, hrn, new_key)
+           (pointer, new_key_pointer) = api.driver.update (record.__dict__, new_record.__dict__, hrn, new_key)
         except:
            pass
         if new_key and new_key_pointer:
@@ -421,7 +474,7 @@ class RegistryManager:
 
         dbsession.commit()
         # update membership for researchers, pis, owners, operators
-        self.update_driver_relations (record, new_record, dbsession)
+        self.update_driver_relations (api, record, new_record)
         
         return 1 
     
@@ -459,7 +512,7 @@ class RegistryManager:
 
         # call testbed callback first
         # IIUC this is done on the local testbed TOO because of the refreshpeer link
-        if not self.driver.remove(record.__dict__):
+        if not api.driver.remove(record.__dict__):
             logger.warning("driver.remove failed")
 
         # delete from sfa db
@@ -474,10 +527,10 @@ class RegistryManager:
         # verify that the callers's ip address exist in the db and is an interface
         # for a node in the db
         (ip, port) = api.remote_addr
-        interfaces = self.driver.shell.GetInterfaces({'ip': ip}, ['node_id'])
+        interfaces = api.driver.shell.GetInterfaces({'ip': ip}, ['node_id'])
         if not interfaces:
             raise NonExistingRecord("no such ip %(ip)s" % locals())
-        nodes = self.driver.shell.GetNodes([interfaces[0]['node_id']], ['node_id', 'hostname'])
+        nodes = api.driver.shell.GetNodes([interfaces[0]['node_id']], ['node_id', 'hostname'])
         if not nodes:
             raise NonExistingRecord("no such node using ip %(ip)s" % locals())
         node = nodes[0]