Merge branch 'geni-v3' of ssh://git.onelab.eu/git/sfa into geni-v3
[sfa.git] / sfa / iotlab / iotlabslices.py
index d66d1e5..3b4258f 100644 (file)
@@ -2,7 +2,7 @@
 This file defines the IotlabSlices class by which all the slice checkings
 upon lease creation are done.
 """
-from sfa.util.xrn import get_authority, urn_to_hrn
+from sfa.util.xrn import get_authority, urn_to_hrn, hrn_to_urn
 from sfa.util.sfalogging import logger
 
 MAXINT = 2L**31-1
@@ -45,7 +45,7 @@ class IotlabSlices:
         slice_authority = get_authority(hrn)
         #Iotlab stuff
         #This slice belongs to the current site
-        if slice_authority == self.driver.iotlab_api.root_auth:
+        if slice_authority == self.driver.testbed_shell.root_auth:
             site_authority = slice_authority
             return None
 
@@ -58,7 +58,7 @@ class IotlabSlices:
 
         # check if we are already peered with this site_authority
         #if so find the peer record
-        peers = self.driver.iotlab_api.GetPeers(peer_filter=site_authority)
+        peers = self.driver.GetPeers(peer_filter=site_authority)
         for peer_record in peers:
             if site_authority == peer_record.hrn:
                 peer = peer_record
@@ -108,7 +108,7 @@ class IotlabSlices:
         logger.debug("IOTLABSLICES verify_slice_leases sfa_slice %s "
                      % (sfa_slice))
         #First get the list of current leases from OAR
-        leases = self.driver.iotlab_api.GetLeases({'name': sfa_slice['hrn']})
+        leases = self.driver.GetLeases({'slice_hrn': sfa_slice['hrn']})
         logger.debug("IOTLABSLICES verify_slice_leases requested_jobs_dict %s \
                         leases %s " % (requested_jobs_dict, leases))
 
@@ -130,7 +130,12 @@ class IotlabSlices:
 
         #First remove job whose duration is too short
         for job in requested_jobs_dict.values():
-            if job['duration'] < self.driver.iotlab_api.GetLeaseGranularity():
+            job['duration'] = \
+                str(int(job['duration']) \
+                * self.driver.testbed_shell.GetLeaseGranularity())
+            if job['duration'] < \
+                    self.driver.testbed_shell.GetLeaseGranularity():
+                logger.debug("JORDAN removed too short lease !!!! %r" % (requested_jobs_dict[job['start_time']],))
                 del requested_jobs_dict[job['start_time']]
 
         #Requested jobs
@@ -161,6 +166,7 @@ class IotlabSlices:
                          "%( start_time))
             if start_time in current_nodes_reserved_by_start_time:
 
+                # JORDAN : if we request the same nodes: do nothing
                 if requested_nodes_by_start_time[start_time] == \
                     current_nodes_reserved_by_start_time[start_time]:
                     continue
@@ -204,27 +210,39 @@ class IotlabSlices:
                 logger.debug("IOTLABSLICES \
                               NEWLEASE slice %s  job %s"
                              % (sfa_slice, job))
-                self.driver.iotlab_api.AddLeases(
+                job_id = self.driver.AddLeases(
                     job['hostname'],
                     sfa_slice, int(job['start_time']),
                     int(job['duration']))
+                # Removed by jordan
+                #if job_id is not None:
+                #    new_leases = self.driver.GetLeases(login=
+                #        sfa_slice['login'])
+                #    for new_lease in new_leases:
+                #        leases.append(new_lease)
 
         #Deleted leases are the ones with lease id not declared in the Rspec
         if deleted_leases:
-            self.driver.iotlab_api.DeleteLeases(deleted_leases,
-                                                sfa_slice['user']['uid'])
+            self.driver.testbed_shell.DeleteLeases(deleted_leases,
+                                                sfa_slice['login'])
+            #self.driver.testbed_shell.DeleteLeases(deleted_leases,
+            #                                    sfa_slice['user']['uid'])
             logger.debug("IOTLABSLICES \
                           verify_slice_leases slice %s deleted_leases %s"
                          % (sfa_slice, deleted_leases))
 
         if reschedule_jobs_dict:
+            logger.debug("JORDAN re-schedule jobs: %r" % (reschedule_jobs_dict,))
             for start_time in reschedule_jobs_dict:
                 job = reschedule_jobs_dict[start_time]
-                self.driver.iotlab_api.AddLeases(
+                self.driver.AddLeases(
                     job['hostname'],
                     sfa_slice, int(job['start_time']),
                     int(job['duration']))
-        return leases
+
+        # Added by Jordan: until we find a better solution, always update the list of leases
+        return self.driver.GetLeases(login= sfa_slice['login'])
+        #return leases
 
     def verify_slice_nodes(self, sfa_slice, requested_slivers, peer):
         """Check for wanted and unwanted nodes in the slice.
@@ -256,7 +274,7 @@ class IotlabSlices:
         deleted_nodes = []
 
         if 'node_ids' in sfa_slice:
-            nodes = self.driver.iotlab_api.GetNodes(
+            nodes = self.driver.testbed_shell.GetNodes(
                 sfa_slice['list_node_ids'],
                 ['hostname'])
             current_slivers = [node['hostname'] for node in nodes]
@@ -271,7 +289,7 @@ class IotlabSlices:
 
             if deleted_nodes:
                 #Delete the entire experience
-                self.driver.iotlab_api.DeleteSliceFromNodes(sfa_slice)
+                self.driver.testbed_shell.DeleteSliceFromNodes(sfa_slice)
             return nodes
 
     def verify_slice(self, slice_hrn, slice_record, sfa_peer):
@@ -296,61 +314,102 @@ class IotlabSlices:
         """
 
         slicename = slice_hrn
+        sfa_slice = None
+
         # check if slice belongs to Iotlab
-        slices_list = self.driver.iotlab_api.GetSlices(
-            slice_filter=slicename, slice_filter_type='slice_hrn')
+        if slicename.startswith("iotlab"):
+            slices_list = self.driver.GetSlices(slice_filter=slicename,
+                                                slice_filter_type='slice_hrn')
 
-        sfa_slice = None
 
-        if slices_list:
-            for sl in slices_list:
+            if slices_list:
+                for sl in slices_list:
 
-                logger.debug("SLABSLICE \t verify_slice slicename %s \
-                                slices_list %s sl %s \r slice_record %s"
-                             % (slicename, slices_list, sl, slice_record))
-                sfa_slice = sl
-                sfa_slice.update(slice_record)
+                    logger.debug("IOTLABSLICES \t verify_slice slicename %s \
+                                    slices_list %s sl %s \r slice_record %s"
+                                 % (slicename, slices_list, sl, slice_record))
+                    sfa_slice = sl
+                    sfa_slice.update(slice_record)
 
+            else:
+                #Search for user in ldap based on email SA 14/11/12
+                ldap_user = self.driver.testbed_shell.ldap.LdapFindUser(\
+                                                        slice_record['user'])
+                logger.debug(" IOTLABSLICES \tverify_slice Oups \
+                            slice_record %s sfa_peer %s ldap_user %s"
+                            % (slice_record, sfa_peer, ldap_user))
+                #User already registered in ldap, meaning user should be in SFA db
+                #and hrn = sfa_auth+ uid
+                sfa_slice = {'hrn': slicename,
+                             'node_list': [],
+                             'authority': slice_record['authority'],
+                             'gid': slice_record['gid'],
+                             'slice_id': slice_record['record_id'],
+                             'urn': hrn_to_urn(slicename,'slice'),
+                             'reg-researchers': slice_record['reg-researchers'],
+                             #'peer_authority': str(sfa_peer)
+                             }
+
+                if ldap_user:
+                    hrn = self.driver.testbed_shell.root_auth + '.' \
+                                                    + ldap_user['uid']
+                    # Registry returns a user record from get_user_record 
+                    user = self.driver.get_user_record(hrn)
+
+                    logger.debug(" IOTLABSLICES \tverify_slice hrn %s USER %s"
+                                 % (hrn, user))
+
+                     # add the external slice to the local SFA iotlab DB
+                    if sfa_slice:
+                        self.driver.AddSlice(sfa_slice, user)
+
+                logger.debug("IOTLABSLICES \tverify_slice ADDSLICE OK")
         else:
-            #Search for user in ldap based on email SA 14/11/12
-            ldap_user = self.driver.iotlab_api.ldap.LdapFindUser(\
-                                                    slice_record['user'])
-            logger.debug(" IOTLABSLICES \tverify_slice Oups \
-                        slice_record %s sfa_peer %s ldap_user %s"
-                        % (slice_record, sfa_peer, ldap_user))
-            #User already registered in ldap, meaning user should be in SFA db
-            #and hrn = sfa_auth+ uid
             sfa_slice = {'hrn': slicename,
-                         'node_list': [],
-                         'authority': slice_record['authority'],
-                         'gid': slice_record['gid'],
-                         'slice_id': slice_record['record_id'],
-                         'reg-researchers': slice_record['reg-researchers'],
-                         'peer_authority': str(sfa_peer)
-                         }
-
-            if ldap_user:
-                hrn = self.driver.iotlab_api.root_auth + '.' + ldap_user['uid']
-                user = self.driver.get_user_record(hrn)
-
-                logger.debug(" IOTLABSLICES \tverify_slice hrn %s USER %s"
-                             % (hrn, user))
-
-                 # add the external slice to the local SFA iotlab DB
-                if sfa_slice:
-                    self.driver.iotlab_api.AddSlice(sfa_slice, user)
-
-            logger.debug("IOTLABSLICES \tverify_slice ADDSLICE OK")
+             'node_list': [],
+             'authority': slice_record['authority'],
+             'gid': slice_record['gid'],
+             'urn': hrn_to_urn(slicename,'slice'),
+             #'slice_id': slice_record['record_id'],
+             'reg-researchers': slice_record['reg-researchers'],
+             #'peer_authority': str(sfa_peer)
+            }
+
+
+            # JORDAN
+            logger.debug("JORDAN ADDSLICE")
+            logger.debug("ADDSLICE user hrn = %s" % slice_record['user']['hrn'])
+            # XXX LOIC !!! searching in IOTLAB DB because has been added in verify_persons
+            user = self.driver.get_user_record(slice_record['user']['hrn'])
+
+            logger.debug("LOIC ADDSLICE Search in IOTLAB DB for user = %s" % user)
+            # XXX LOIC !!! not searching in LDAP because this has been done in verify_persons
+            #ldap_user = self.driver.testbed_shell.ldap.LdapFindUser(\
+            #                                        slice_record['user'])
+            #if ldap_user:
+            #    hrn = self.driver.testbed_shell.root_auth + '.' \
+            #                                    + ldap_user['uid']
+            #    user = self.driver.get_user_record(hrn)
+            #    logger.debug(" IOTLAB SLICES JORDAN user: %r %r " % (user, hrn))
+
+            #    logger.debug(" IOTLABSLICES \tverify_slice hrn %s USER %s"
+            #                 % (hrn, user))
+
+            # add the external slice to the local SFA iotlab DB
+            if sfa_slice:
+                self.driver.AddSlice(sfa_slice, user)
+                logger.debug("IOTLABSLICES \tverify_slice ADDSLICE OK")
+
         return sfa_slice
 
 
-    def verify_persons(self, slice_hrn, slice_record, users, options={}):
+    def verify_persons(self, slice_hrn, slice_record, users, options=None):
         """Ensures the users in users list exist and are enabled in LDAP. Adds
-        person if needed.
+        person if needed (AddPerson).
 
         Checking that a user exist is based on the user's email. If the user is
         still not found in the LDAP, it means that the user comes from another
-        federated. In this case an account has to be created in LDAP
+        federated testbed. In this case an account has to be created in LDAP
         so as to enable the user to use the testbed, since we trust the testbed
         he comes from. This is done by calling AddPerson.
 
@@ -358,8 +417,8 @@ class IotlabSlices:
         :param slice_record: record of the slice_hrn
         :param users: users is a record list. Records can either be
             local records or users records from known and trusted federated
-            sites.If the user is from another site that iotlab doesn't trust yet,
-            then Resolve will raise an error before getting to create_sliver.
+            sites.If the user is from another site that iotlab doesn't trust
+            yet, then Resolve will raise an error before getting to allocate.
 
         :type slice_hrn: string
         :type slice_record: string
@@ -370,12 +429,10 @@ class IotlabSlices:
 
 
         """
-        #TODO SA 21/08/12 verify_persons Needs review
-
+        if options is None: options={}
         logger.debug("IOTLABSLICES \tverify_persons \tslice_hrn  %s  \
                     \t slice_record %s\r\n users %s \t  "
                      % (slice_hrn, slice_record, users))
-        users_by_id = {}
 
         users_by_email = {}
         #users_dict : dict whose keys can either be the user's hrn or its id.
@@ -384,18 +441,18 @@ class IotlabSlices:
 
         #First create dicts by hrn and id for each user in the user record list:
         for info in users:
-            if 'slice_record' in info:
-                slice_rec = info['slice_record']
-                user = slice_rec['user']
+            # if 'slice_record' in info:
+            #     slice_rec = info['slice_record']
+                # if 'user' in slice_rec :
+                #     user = slice_rec['user']
 
-            if 'email' in user:
-                users_by_email[user['email']] = user
-                users_dict[user['email']] = user
+            if 'email' in info:
+                users_by_email[info['email']] = info
+                users_dict[info['email']] = info
 
-        logger.debug("SLABSLICE.PY \t verify_person  \
-                        users_dict %s \r\n user_by_email %s \r\n \
-                        \tusers_by_id %s "
-                     % (users_dict, users_by_email, users_by_id))
+        logger.debug("IOTLABSLICES.PY \t verify_person  \
+                        users_dict %s \r\n user_by_email %s \r\n  "
+                     % (users_dict, users_by_email))
 
         existing_user_ids = []
         existing_user_emails = []
@@ -412,76 +469,142 @@ class IotlabSlices:
             #Check user i in LDAP with GetPersons
             #Needed because what if the user has been deleted in LDAP but
             #is still in SFA?
-            existing_users = self.driver.iotlab_api.GetPersons(filter_user)
-            logger.debug(" \r\n SLABSLICE.PY \tverify_person  filter_user \
-                        %s existing_users %s "
+            existing_users = self.driver.testbed_shell.GetPersons(filter_user)
+            logger.debug(" \r\n IOTLABSLICES.PY \tverify_person  filter_user %s\
+                       existing_users %s  "
                         % (filter_user, existing_users))
-            #User is in iotlab LDAP
-            if existing_users:
+            # User is in iotlab LDAP
+            # XXX LOIC !!! user is existing in iotlab LDAP but coming from OneLab portal
+            if existing_users and slice_hrn.startswith("iotlab"):
                 for user in existing_users:
+                    user['login'] = user['uid']
                     users_dict[user['email']].update(user)
                     existing_user_emails.append(
                         users_dict[user['email']]['email'])
 
-
             # User from another known trusted federated site. Check
             # if a iotlab account matching the email has already been created.
             else:
                 req = 'mail='
                 if isinstance(users, list):
                     req += users[0]['email']
+                    user = users[0]
                 else:
                     req += users['email']
-                ldap_reslt = self.driver.iotlab_api.ldap.LdapSearch(req)
+                    user = users
+                ldap_reslt = self.driver.testbed_shell.ldap.LdapSearch(req)
 
                 if ldap_reslt:
-                    logger.debug(" SLABSLICE.PY \tverify_person users \
+                    logger.debug(" IOTLABSLICES.PY \tverify_person users \
                                 USER already in Iotlab \t ldap_reslt %s \
                                 " % (ldap_reslt))
-                    existing_users.append(ldap_reslt[1])
-
+                    
+                    #existing_users.append(ldap_reslt[1])
+                    # XXX LOIC !!! Not sure why we use to take the element 1
+                    if len(ldap_reslt)>0:
+                        ldap_reslt = ldap_reslt[0]
+                        logger.debug(ldap_reslt)
+                        if len(ldap_reslt)>1:
+                            ldap_reslt = ldap_reslt[1]
+                            logger.debug("LOIC - iotlabslices.py - ldap_reslt = %s" % (ldap_reslt))
+                            existing_users.append(ldap_reslt)
+                            existing_user_emails.append(ldap_reslt['mail'][0])
+                    
+                    # XXX LOIC !!! This login is required 
+                    # sfa/iotlab/iotlabdriver.py", line 523, in AddLeases 
+                    if 'uid' in ldap_reslt:
+                        # meaning that the Person was found in LDAP
+                        slice_record['login'] = ldap_reslt['uid'][0]
+
+                    # XXX LOIC !!! Add the user to IOTLAB DB Registry???
+                    #if 'keys' in user:
+                    #    user['pkey'] = user['keys'][0]
+
+                    #ret = self.driver.AddPerson(user,add_to_ldap=False)
+                    #logger.debug("LOIC verify_persons AddPerson ret = %s" % ret)
                 else:
                     #User not existing in LDAP
-                    logger.debug("SLABSLICE.PY \tverify_person users \
+                    logger.debug("IOTLABSLICES.PY \tverify_person users \
                                 not in ldap ...NEW ACCOUNT NEEDED %s \r\n \t \
                                 ldap_reslt %s " % (users, ldap_reslt))
-
         requested_user_emails = users_by_email.keys()
-        requested_user_hrns = \
-            [users_by_email[user]['hrn'] for user in users_by_email]
-        logger.debug("SLABSLICE.PY \tverify_person  \
-                       users_by_email  %s " % (users_by_email))
+        requested_user_hrns = \
+            [users_by_email[user]['hrn'] for user in users_by_email]
+        # logger.debug("IOTLABSLICES.PY \tverify_person  \
+                       users_by_email  %s " % (users_by_email))
 
-        #Check that the user of the slice in the slice record
-        #matches one of the existing users
-        try:
-            if slice_record['PI'][0] in requested_user_hrns:
-                logger.debug(" SLABSLICE  \tverify_person ['PI']\
-                                slice_record %s" % (slice_record))
+        # #Check that the user of the slice in the slice record
+        # #matches one of the existing users
+        try:
+        #     if slice_record['reg-researchers'][0] in requested_user_hrns:
+        #         logger.debug(" IOTLABSLICES  \tverify_person ['PI']\
+                                slice_record %s" % (slice_record))
 
-        except KeyError:
-            pass
+        except KeyError:
+            pass
 
         # users to be added, removed or updated
         #One user in one iotlab slice : there should be no need
         #to remove/ add any user from/to a slice.
         #However a user from SFA which is not registered in Iotlab yet
         #should be added to the LDAP.
+        logger.debug("LOIC - iotlabslice.py - requested_user_emails = %r" % requested_user_emails)
+        logger.debug("LOIC - iotlabslice.py - existing_user_emails = %r" % existing_user_emails)
+
         added_user_emails = set(requested_user_emails).\
                                         difference(set(existing_user_emails))
-
-
+        logger.debug("LOIC - iotlabslice.py - added_user_emails = %r" % added_user_emails)
+        logger.debug("LOIC - iotlabslice.py - existing_user_emails = %r" % existing_user_emails)
         #self.verify_keys(existing_slice_users, updated_users_list, \
                                                             #peer, append)
 
+        # XXX JORDAN the uid of the user is put in slice_record['login']
+
         added_persons = []
         # add new users
         #requested_user_email is in existing_user_emails
         if len(added_user_emails) == 0:
-            slice_record['login'] = users_dict[requested_user_emails[0]]['uid']
-            logger.debug(" SLABSLICE  \tverify_person QUICK DIRTY %s"
+#            slice_record['login'] = users_dict[requested_user_emails[0]]['uid']
+            logger.debug(" IOTLABSLICES  \tverify_person QUICK DIRTY %s"
                          % (slice_record))
 
+            # XXX JORDAN uid == 'register'
+#            new_hrn = slice_record['user']['hrn']
+#            new_user = self.driver.get_user_record(new_hrn)
+#            if not new_user:
+#                # XXX HERE WE SHOULD CREATE A SFA USER !!!!!!
+#                added_user = users_dict[requested_user_emails[0]]
+#                person = {}
+#                person['peer_person_id'] = None
+#                k_list = ['first_name', 'last_name', 'person_id']
+#                for k in k_list:
+#                    if k in added_user:
+#                        person[k] = added_user[k]
+#                # bug user without key
+#                if added_user['keys']:
+#                    person['pkey'] = added_user['keys'][0]
+#                person['mail'] = added_user['email']
+#                person['email'] = added_user['email']
+#                person['key_ids'] = added_user.get('key_ids', [])
+#                # LOIC !!! il faudrait transformer onelab.upmc.XXX en iotlab.XXX
+#                if new_hrn.startswith("iotlab"):
+#                    person['hrn'] = new_hrn                 
+#                else:
+#                    hrn_hierarchy = new_hrn.split(".")
+#                    person['hrn'] = "iotlab." + hrn_hierarchy[-1]
+#
+#                ret = self.driver.AddPerson(person, add_to_ldap=False)
+#                logger.debug("AddPerson return = %r type = %s" % (ret,type(ret)))
+#                # LOIC !!! XXX Dans un cas ça retourne un dict du LDAP dans l'autre cas pas de LDAP donc ça ne retourne rien
+#                if ret is None:
+#                    person['uid'] = slice_record['login']
+#                else:
+#                    if 'uid' in ret:
+#                        # meaning bool is True and the AddPerson was successful
+#                        person['uid'] = ret['uid']
+#                        slice_record['login'] = person['uid']
+
+        # XXX JORDAN i have no added_user_emails
         for added_user_email in added_user_emails:
             added_user = users_dict[added_user_email]
             logger.debug(" IOTLABSLICES \r\n \r\n  \t  verify_person \
@@ -492,13 +615,14 @@ class IotlabSlices:
             for k in k_list:
                 if k in added_user:
                     person[k] = added_user[k]
-
-            person['pkey'] = added_user['keys'][0]
+            # bug user without key
+            if added_user['keys']:
+                person['pkey'] = added_user['keys'][0]
             person['mail'] = added_user['email']
             person['email'] = added_user['email']
             person['key_ids'] = added_user.get('key_ids', [])
 
-            ret = self.driver.iotlab_api.AddPerson(person)
+            ret = self.driver.AddPerson(person)
             if 'uid' in ret:
                 # meaning bool is True and the AddPerson was successful
                 person['uid'] = ret['uid']
@@ -507,7 +631,7 @@ class IotlabSlices:
                 # error message in ret
                 logger.debug(" IOTLABSLICES ret message %s" %(ret))
 
-            logger.debug(" SLABSLICE \r\n \r\n  \t THE SECOND verify_person\
+            logger.debug(" IOTLABSLICES \r\n \r\n  \t THE SECOND verify_person\
                            person %s" % (person))
             #Update slice_Record with the id now known to LDAP
 
@@ -516,15 +640,16 @@ class IotlabSlices:
         return added_persons
 
 
-    def verify_keys(self, persons, users, peer, options={}):
+    def verify_keys(self, persons, users, peer, options=None):
         """
         .. warning:: unused
         """
+        if options is None: options={}
         # existing keys
         key_ids = []
         for person in persons:
             key_ids.extend(person['key_ids'])
-        keylist = self.driver.iotlab_api.GetKeys(key_ids, ['key_id', 'key'])
+        keylist = self.driver.GetKeys(key_ids, ['key_id', 'key'])
 
         keydict = {}
         for key in keylist:
@@ -550,34 +675,28 @@ class IotlabSlices:
                     #try:
                         ##if peer:
                             #person = persondict[user['email']]
-                            #self.driver.iotlab_api.UnBindObjectFromPeer(
+                            #self.driver.testbed_shell.UnBindObjectFromPeer(
                                 # 'person',person['person_id'],
                                 # peer['shortname'])
-                    ret = self.driver.iotlab_api.AddPersonKey(
+                    ret = self.driver.testbed_shell.AddPersonKey(
                         user['email'], key)
                         #if peer:
                             #key_index = user_keys.index(key['key'])
                             #remote_key_id = user['key_ids'][key_index]
-                            #self.driver.iotlab_api.BindObjectToPeer('key', \
+                            #self.driver.testbed_shell.BindObjectToPeer('key', \
                                             #key['key_id'], peer['shortname'], \
                                             #remote_key_id)
 
-                    #finally:
-                        #if peer:
-                            #self.driver.iotlab_api.BindObjectToPeer('person', \
-                                    #person['person_id'], peer['shortname'], \
-                                    #user['person_id'])
-
         # remove old keys (only if we are not appending)
         append = options.get('append', True)
         if append is False:
             removed_keys = set(existing_keys).difference(requested_keys)
             for key in removed_keys:
                     #if peer:
-                        #self.driver.iotlab_api.UnBindObjectFromPeer('key', \
+                        #self.driver.testbed_shell.UnBindObjectFromPeer('key', \
                                         #key, peer['shortname'])
 
                 user = users_by_key_string[key]
-                self.driver.iotlab_api.DeleteKey(user, key)
+                self.driver.testbed_shell.DeleteKey(user, key)
 
         return