2 This file defines the IotlabSlices class by which all the slice checkings
3 upon lease creation are done.
5 from sfa.util.xrn import get_authority, urn_to_hrn, hrn_to_urn
6 from sfa.util.sfalogging import logger
13 This class is responsible for checking the slice when creating a
14 lease or a sliver. Those checks include verifying that the user is valid,
15 that the slice is known from the testbed or from our peers, that the list
16 of nodes involved has not changed (in this case the lease is modified
19 rspec_to_slice_tag = {'max_rate': 'net_max_rate'}
21 def __init__(self, driver):
23 Get the reference to the driver here.
27 def get_peer(self, xrn):
29 Finds the authority of a resource based on its xrn.
30 If the authority is Iotlab (local) return None,
31 Otherwise, look up in the DB if Iotlab is federated with this site
32 authority and returns its DB record if it is the case.
34 :param xrn: resource's xrn
40 hrn, hrn_type = urn_to_hrn(xrn)
41 #Does this slice belong to a local site or a peer iotlab site?
44 # get this slice's authority (site)
45 slice_authority = get_authority(hrn)
47 #This slice belongs to the current site
48 if slice_authority == self.driver.testbed_shell.root_auth:
49 site_authority = slice_authority
52 site_authority = get_authority(slice_authority).lower()
53 # get this site's authority (sfa root authority or sub authority)
55 logger.debug("IOTLABSLICES \t get_peer slice_authority %s \
56 site_authority %s hrn %s"
57 % (slice_authority, site_authority, hrn))
59 # check if we are already peered with this site_authority
60 #if so find the peer record
61 peers = self.driver.GetPeers(peer_filter=site_authority)
62 for peer_record in peers:
63 if site_authority == peer_record.hrn:
65 logger.debug(" IOTLABSLICES \tget_peer peer %s " % (peer))
68 def get_sfa_peer(self, xrn):
69 """Returns the authority name for the xrn or None if the local site
72 :param xrn: the xrn of the resource we are looking the authority for.
74 :returns: the resources's authority name.
78 hrn, hrn_type = urn_to_hrn(xrn)
80 # return the authority for this hrn or None if we are the authority
82 slice_authority = get_authority(hrn)
83 site_authority = get_authority(slice_authority)
85 if site_authority != self.driver.hrn:
86 sfa_peer = site_authority
90 def verify_slice_leases(self, sfa_slice, requested_jobs_dict, peer):
92 Compare requested leases with the leases already scheduled/
93 running in OAR. If necessary, delete and recreate modified leases,
94 and delete no longer requested ones.
96 :param sfa_slice: sfa slice record
97 :param requested_jobs_dict: dictionary of requested leases
98 :param peer: sfa peer record
100 :type sfa_slice: dict
101 :type requested_jobs_dict: dict
103 :returns: leases list of dictionary
108 logger.debug("IOTLABSLICES verify_slice_leases sfa_slice %s "
110 #First get the list of current leases from OAR
111 leases = self.driver.GetLeases({'slice_hrn': sfa_slice['hrn']})
112 logger.debug("IOTLABSLICES verify_slice_leases requested_jobs_dict %s \
113 leases %s " % (requested_jobs_dict, leases))
115 current_nodes_reserved_by_start_time = {}
116 requested_nodes_by_start_time = {}
117 leases_by_start_time = {}
118 reschedule_jobs_dict = {}
120 #Create reduced dictionary with key start_time and value
122 #-for the leases already registered by OAR first
123 # then for the new leases requested by the user
125 #Leases already scheduled/running in OAR
127 current_nodes_reserved_by_start_time[lease['t_from']] = \
128 lease['reserved_nodes']
129 leases_by_start_time[lease['t_from']] = lease
131 #First remove job whose duration is too short
132 for job in requested_jobs_dict.values():
134 str(int(job['duration']) \
135 * self.driver.testbed_shell.GetLeaseGranularity())
136 if job['duration'] < \
137 self.driver.testbed_shell.GetLeaseGranularity():
138 del requested_jobs_dict[job['start_time']]
141 for start_time in requested_jobs_dict:
142 requested_nodes_by_start_time[int(start_time)] = \
143 requested_jobs_dict[start_time]['hostname']
144 #Check if there is any difference between the leases already
145 #registered in OAR and the requested jobs.
146 #Difference could be:
147 #-Lease deleted in the requested jobs
148 #-Added/removed nodes
151 logger.debug("IOTLABSLICES verify_slice_leases \
152 requested_nodes_by_start_time %s \
153 "% (requested_nodes_by_start_time))
154 #Find all deleted leases
156 list(set(leases_by_start_time.keys()).\
157 difference(requested_nodes_by_start_time.keys()))
158 deleted_leases = [leases_by_start_time[start_time]['lease_id'] \
159 for start_time in start_time_list]
162 #Find added or removed nodes in exisiting leases
163 for start_time in requested_nodes_by_start_time:
164 logger.debug("IOTLABSLICES verify_slice_leases start_time %s \
166 if start_time in current_nodes_reserved_by_start_time:
168 # JORDAN : if we request the same nodes: do nothing
169 if requested_nodes_by_start_time[start_time] == \
170 current_nodes_reserved_by_start_time[start_time]:
175 set(requested_nodes_by_start_time[start_time])
177 update_node_set.difference(\
178 current_nodes_reserved_by_start_time[start_time])
180 update_node_set.intersection(\
181 current_nodes_reserved_by_start_time[start_time])
184 current_nodes_reserved_by_start_time[start_time])
186 old_nodes_set.difference(\
187 requested_nodes_by_start_time[start_time])
188 logger.debug("IOTLABSLICES verify_slice_leases \
189 shared_nodes %s added_nodes %s removed_nodes %s"\
190 %(shared_nodes, added_nodes,removed_nodes ))
191 #If the lease is modified, delete it before
193 #Add the deleted lease job id in the list
194 #WARNING :rescheduling does not work if there is already
195 # 2 running/scheduled jobs because deleting a job
196 #takes time SA 18/10/2012
197 if added_nodes or removed_nodes:
198 deleted_leases.append(\
199 leases_by_start_time[start_time]['lease_id'])
201 if added_nodes or shared_nodes:
202 reschedule_jobs_dict[str(start_time)] = \
203 requested_jobs_dict[str(start_time)]
208 job = requested_jobs_dict[str(start_time)]
209 logger.debug("IOTLABSLICES \
210 NEWLEASE slice %s job %s"
212 job_id = self.driver.AddLeases(
214 sfa_slice, int(job['start_time']),
215 int(job['duration']))
218 #if job_id is not None:
219 # new_leases = self.driver.GetLeases(login=
220 # sfa_slice['login'])
221 # for new_lease in new_leases:
222 # leases.append(new_lease)
224 #Deleted leases are the ones with lease id not declared in the Rspec
226 self.driver.testbed_shell.DeleteLeases(deleted_leases,
228 #self.driver.testbed_shell.DeleteLeases(deleted_leases,
229 # sfa_slice['user']['uid'])
230 logger.debug("IOTLABSLICES \
231 verify_slice_leases slice %s deleted_leases %s"
232 % (sfa_slice, deleted_leases))
234 if reschedule_jobs_dict:
235 for start_time in reschedule_jobs_dict:
236 job = reschedule_jobs_dict[start_time]
237 self.driver.AddLeases(
239 sfa_slice, int(job['start_time']),
240 int(job['duration']))
242 # Added by Jordan: until we find a better solution, always update the list of leases
243 return self.driver.GetLeases(login= sfa_slice['login'])
246 def verify_slice_nodes(self, sfa_slice, requested_slivers, peer):
247 """Check for wanted and unwanted nodes in the slice.
249 Removes nodes and associated leases that the user does not want anymore
250 by deleteing the associated job in OAR (DeleteSliceFromNodes).
251 Returns the nodes' hostnames that are going to be in the slice.
253 :param sfa_slice: slice record. Must contain node_ids and list_node_ids.
255 :param requested_slivers: list of requested nodes' hostnames.
256 :param peer: unused so far.
258 :type sfa_slice: dict
259 :type requested_slivers: list
262 :returns: list requested nodes hostnames
265 .. warning:: UNUSED SQA 24/07/13
266 .. seealso:: DeleteSliceFromNodes
267 .. todo:: check what to do with the peer? Can not remove peer nodes from
268 slice here. Anyway, in this case, the peer should have gotten the
275 if 'node_ids' in sfa_slice:
276 nodes = self.driver.testbed_shell.GetNodes(
277 sfa_slice['list_node_ids'],
279 current_slivers = [node['hostname'] for node in nodes]
281 # remove nodes not in rspec
282 deleted_nodes = list(set(current_slivers).
283 difference(requested_slivers))
285 logger.debug("IOTLABSLICES \tverify_slice_nodes slice %s\
286 \r\n \r\n deleted_nodes %s"
287 % (sfa_slice, deleted_nodes))
290 #Delete the entire experience
291 self.driver.testbed_shell.DeleteSliceFromNodes(sfa_slice)
294 def verify_slice(self, slice_hrn, slice_record, sfa_peer):
295 """Ensures slice record exists.
297 The slice record must exist either in Iotlab or in the other
298 federated testbed (sfa_peer). If the slice does not belong to Iotlab,
299 check if the user already exists in LDAP. In this case, adds the slice
300 to the sfa DB and associates its LDAP user.
302 :param slice_hrn: slice's name
303 :param slice_record: sfa record of the slice
304 :param sfa_peer: name of the peer authority if any.(not Iotlab).
306 :type slice_hrn: string
307 :type slice_record: dictionary
308 :type sfa_peer: string
310 .. seealso:: AddSlice
315 slicename = slice_hrn
318 # check if slice belongs to Iotlab
319 if slicename.startswith("iotlab"):
320 slices_list = self.driver.GetSlices(slice_filter=slicename,
321 slice_filter_type='slice_hrn')
324 for sl in slices_list:
326 logger.debug("IOTLABSLICES \t verify_slice slicename %s \
327 slices_list %s sl %s \r slice_record %s"
328 % (slicename, slices_list, sl, slice_record))
330 sfa_slice.update(slice_record)
333 #Search for user in ldap based on email SA 14/11/12
334 ldap_user = self.driver.testbed_shell.ldap.LdapFindUser(\
335 slice_record['user'])
336 logger.debug(" IOTLABSLICES \tverify_slice Oups \
337 slice_record %s sfa_peer %s ldap_user %s"
338 % (slice_record, sfa_peer, ldap_user))
339 #User already registered in ldap, meaning user should be in SFA db
340 #and hrn = sfa_auth+ uid
341 sfa_slice = {'hrn': slicename,
343 'authority': slice_record['authority'],
344 'gid': slice_record['gid'],
345 #'slice_id': slice_record['record_id'],
346 'reg-researchers': slice_record['reg-researchers'],
347 'urn': hrn_to_urn(slicename,'slice'),
348 #'peer_authority': str(sfa_peer)
352 # hrn = self.driver.testbed_shell.root_auth + '.' \
354 for hrn in slice_record['reg-researchers']:
355 user = self.driver.get_user_record(hrn)
359 logger.debug(" IOTLABSLICES \tverify_slice hrn %s USER %s"
362 # add the external slice to the local SFA iotlab DB
364 self.driver.AddSlice(sfa_slice, user)
366 logger.debug("IOTLABSLICES \tverify_slice ADDSLICE OK")
370 def verify_persons(self, slice_hrn, slice_record, users, options=None):
371 """Ensures the users in users list exist and are enabled in LDAP. Adds
372 person if needed (AddPerson).
374 Checking that a user exist is based on the user's email. If the user is
375 still not found in the LDAP, it means that the user comes from another
376 federated testbed. In this case an account has to be created in LDAP
377 so as to enable the user to use the testbed, since we trust the testbed
378 he comes from. This is done by calling AddPerson.
380 :param slice_hrn: slice name
381 :param slice_record: record of the slice_hrn
382 :param users: users is a record list. Records can either be
383 local records or users records from known and trusted federated
384 sites.If the user is from another site that iotlab doesn't trust
385 yet, then Resolve will raise an error before getting to allocate.
387 :type slice_hrn: string
388 :type slice_record: string
391 .. seealso:: AddPerson
392 .. note:: Removed unused peer and sfa_peer parameters. SA 18/07/13.
394 if options is None: options={}
395 user = slice_record['user']
396 logger.debug("IOTLABSLICES \tverify_persons \tuser %s " % user)
398 'peer_person_id': None,
399 'mail' : user['email'],
400 'email' : user['email'],
401 'key_ids' : user.get('key_ids', []),
404 if 'first_name' in user:
405 person['first_name'] = user['first_name']
406 if 'last_name' in user:
407 person['last_name'] = user['last_name']
408 if 'person_id' in user:
409 person['person_id'] = user['person_id']
411 # Only one key is kept for IoTLAB
412 person['pkey'] = user['keys'][0]
413 # SFA DB (if user already exist we do nothing)
414 self.driver.add_person_to_db(person)
415 # Iot-LAB LDAP (if user already exist we do nothing)
416 ret = self.driver.AddPerson(person)
417 # user uid information is only in LDAP
418 # Be carreful : global scope of dict slice_record in driver
419 slice_record['login'] = ret['uid']
424 def verify_keys(self, persons, users, peer, options=None):
428 if options is None: options={}
431 for person in persons:
432 key_ids.extend(person['key_ids'])
433 keylist = self.driver.GetKeys(key_ids, ['key_id', 'key'])
437 keydict[key['key']] = key['key_id']
438 existing_keys = keydict.keys()
441 for person in persons:
442 persondict[person['email']] = person
447 users_by_key_string = {}
449 user_keys = user.get('keys', [])
450 updated_persons.append(user)
451 for key_string in user_keys:
452 users_by_key_string[key_string] = user
453 requested_keys.append(key_string)
454 if key_string not in existing_keys:
455 key = {'key': key_string, 'key_type': 'ssh'}
458 #person = persondict[user['email']]
459 #self.driver.testbed_shell.UnBindObjectFromPeer(
460 # 'person',person['person_id'],
462 ret = self.driver.testbed_shell.AddPersonKey(
465 #key_index = user_keys.index(key['key'])
466 #remote_key_id = user['key_ids'][key_index]
467 #self.driver.testbed_shell.BindObjectToPeer('key', \
468 #key['key_id'], peer['shortname'], \
471 # remove old keys (only if we are not appending)
472 append = options.get('append', True)
474 removed_keys = set(existing_keys).difference(requested_keys)
475 for key in removed_keys:
477 #self.driver.testbed_shell.UnBindObjectFromPeer('key', \
478 #key, peer['shortname'])
480 user = users_by_key_string[key]
481 self.driver.testbed_shell.DeleteKey(user, key)