3 from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \
4 RecordNotFound, SfaNotImplemented, SliverDoesNotExist, SearchFailed, \
5 UnsupportedOperation, Forbidden
6 from sfa.util.sfalogging import logger
7 from sfa.util.defaultdict import defaultdict
8 from sfa.util.sfatime import utcparse, datetime_to_string, datetime_to_epoch
9 from sfa.util.xrn import Xrn, hrn_to_urn, get_leaf
10 from sfa.util.cache import Cache
12 # one would think the driver should not need to mess with the SFA db, but..
13 from sfa.storage.model import RegRecord, SliverAllocation
14 from sfa.trust.credential import Credential
16 # used to be used in get_ticket
17 #from sfa.trust.sfaticket import SfaTicket
18 from sfa.rspecs.version_manager import VersionManager
19 from sfa.rspecs.rspec import RSpec
21 # the driver interface, mostly provides default behaviours
22 from sfa.managers.driver import Driver
23 from sfa.planetlab.plshell import PlShell
24 from sfa.planetlab.plaggregate import PlAggregate
25 from sfa.planetlab.plslices import PlSlices
26 from sfa.planetlab.plxrn import PlXrn, slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, top_auth, hash_loginbase
29 def list_to_dict(recs, key):
31 convert a list of dictionaries into a dictionary keyed on the
32 specified dictionary key
34 return { rec[key] : rec for rec in recs }
37 # PlShell is just an xmlrpc serverproxy where methods
38 # can be sent as-is; it takes care of authentication
39 # from the global config
41 class PlDriver (Driver):
43 # the cache instance is a class member so it survives across incoming requests
46 def __init__ (self, api):
47 Driver.__init__ (self, api)
49 self.shell = PlShell (config)
51 if config.SFA_AGGREGATE_CACHING:
52 if PlDriver.cache is None:
53 PlDriver.cache = Cache()
54 self.cache = PlDriver.cache
56 def sliver_to_slice_xrn(self, xrn):
57 sliver_id_parts = Xrn(xrn).get_sliver_id_parts()
58 filter = {'peer_id': None}
60 filter['slice_id'] = int(sliver_id_parts[0])
62 filter['name'] = sliver_id_parts[0]
63 slices = self.shell.GetSlices(filter, ['hrn'])
65 raise Forbidden("Unable to locate slice record for sliver: {}".format(xrn))
67 slice_xrn = slice['hrn']
70 def check_sliver_credentials(self, creds, urns):
71 # build list of cred object hrns
74 slice_cred_hrn = Credential(cred=cred).get_gid_object().get_hrn()
75 top_auth_hrn = top_auth(slice_cred_hrn)
76 site_hrn = '.'.join(slice_cred_hrn.split('.')[:-1])
77 slice_part = slice_cred_hrn.split('.')[-1]
78 if top_auth_hrn == self.hrn:
79 login_base = slice_cred_hrn.split('.')[-2][:12]
81 login_base = hash_loginbase(site_hrn)
83 slicename = '_'.join([login_base, slice_part])
84 slice_cred_names.append(slicename)
86 # look up slice name of slivers listed in urns arg
89 sliver_id_parts = Xrn(xrn=urn).get_sliver_id_parts()
91 slice_ids.append(int(sliver_id_parts[0]))
96 raise Forbidden("sliver urn not provided")
98 slices = self.shell.GetSlices(slice_ids)
99 sliver_names = [slice['name'] for slice in slices]
101 # make sure we have a credential for every specified sliver ierd
102 for sliver_name in sliver_names:
103 if sliver_name not in slice_cred_names:
104 msg = "Valid credential not found for target: {}".format(sliver_name)
107 ########################################
108 ########## registry oriented
109 ########################################
111 def augment_records_with_testbed_info (self, sfa_records):
112 return self.fill_record_info (sfa_records)
115 def register (self, sfa_record, hrn, pub_key):
116 type = sfa_record['type']
117 pl_record = self.sfa_fields_to_pl_fields(type, hrn, sfa_record)
119 if type == 'authority':
120 sites = self.shell.GetSites({'peer_id': None, 'login_base': pl_record['login_base']})
122 # xxx when a site gets registered through SFA we need to set its max_slices
123 if 'max_slices' not in pl_record:
124 pl_record['max_slices'] = 2
125 pointer = self.shell.AddSite(pl_record)
126 self.shell.SetSiteHrn(int(pointer), hrn)
128 pointer = sites[0]['site_id']
130 elif type == 'slice':
131 acceptable_fields = ['url', 'instantiation', 'name', 'description']
132 for key in pl_record.keys():
133 if key not in acceptable_fields:
135 slices = self.shell.GetSlices({'peer_id': None, 'name': pl_record['name']})
137 if not pl_record.get('url', None) or not pl_record.get('description', None):
138 pl_record['url'] = hrn
139 pl_record['description'] = hrn
141 pointer = self.shell.AddSlice(pl_record)
142 self.shell.SetSliceHrn(int(pointer), hrn)
144 pointer = slices[0]['slice_id']
147 persons = self.shell.GetPersons({'peer_id': None, 'email': sfa_record['email']})
149 for key in ['first_name','last_name']:
150 if key not in sfa_record:
151 sfa_record[key] = '*from*sfa*'
152 # AddPerson does not allow everything to be set
153 can_add = ['first_name', 'last_name', 'title','email', 'password', 'phone', 'url', 'bio']
154 add_person_dict = { k : sfa_record[k] for k in sfa_record if k in can_add }
155 pointer = self.shell.AddPerson(add_person_dict)
156 self.shell.SetPersonHrn(int(pointer), hrn)
158 pointer = persons[0]['person_id']
160 # enable the person's account
161 self.shell.UpdatePerson(pointer, {'enabled': True})
162 # add this person to the site
163 login_base = get_leaf(sfa_record['authority'])
164 self.shell.AddPersonToSite(pointer, login_base)
166 # What roles should this user have?
168 if 'roles' in sfa_record:
169 # if specified in xml, but only low-level roles
170 roles = [ role for role in sfa_record['roles'] if role in ['user','tech'] ]
171 # at least user if no other cluse could be found
175 self.shell.AddRoleToPerson(role, pointer)
178 self.shell.AddPersonKey(pointer, {'key_type' : 'ssh', 'key' : pub_key})
181 login_base = PlXrn(xrn=sfa_record['authority'], type='authority').pl_login_base()
182 nodes = self.shell.GetNodes({'peer_id': None, 'hostname': pl_record['hostname']})
184 pointer = self.shell.AddNode(login_base, pl_record)
185 self.shell.SetNodeHrn(int(pointer), hrn)
187 pointer = nodes[0]['node_id']
192 # xxx actually old_sfa_record comes filled with plc stuff as well in the original code
193 def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
194 pointer = old_sfa_record['pointer']
195 type = old_sfa_record['type']
196 new_key_pointer = None
198 # new_key implemented for users only
199 if new_key and type not in [ 'user' ]:
200 raise UnknownSfaType(type)
202 if (type == "authority"):
203 logger.debug("pldriver.update: calling UpdateSite with {}".format(new_sfa_record))
204 self.shell.UpdateSite(pointer, new_sfa_record)
205 self.shell.SetSiteHrn(pointer, hrn)
207 elif type == "slice":
208 pl_record = self.sfa_fields_to_pl_fields(type, hrn, new_sfa_record)
209 if 'name' in pl_record:
210 pl_record.pop('name')
211 self.shell.UpdateSlice(pointer, pl_record)
212 self.shell.SetSliceHrn(pointer, hrn)
215 # SMBAKER: UpdatePerson only allows a limited set of fields to be
216 # updated. Ideally we should have a more generic way of doing
217 # this. I copied the field names from UpdatePerson.py...
219 all_fields = new_sfa_record
220 for key in all_fields.keys():
221 if key in ['first_name', 'last_name', 'title', 'email',
222 'password', 'phone', 'url', 'bio', 'accepted_aup',
224 update_fields[key] = all_fields[key]
225 # when updating a user, we always get a 'email' field at this point
226 # this is because 'email' is a native field in the RegUser object...
227 if 'email' in update_fields and not update_fields['email']:
228 del update_fields['email']
229 self.shell.UpdatePerson(pointer, update_fields)
230 self.shell.SetPersonHrn(pointer, hrn)
233 # must check this key against the previous one if it exists
234 persons = self.shell.GetPersons({'peer_id': None, 'person_id': pointer}, ['key_ids'])
236 keys = person['key_ids']
237 keys = self.shell.GetKeys(person['key_ids'])
241 if new_key == key['key']:
243 new_key_pointer = key['key_id']
246 new_key_pointer = self.shell.AddPersonKey(pointer, {'key_type': 'ssh', 'key': new_key})
249 self.shell.UpdateNode(pointer, new_sfa_record)
251 return (pointer, new_key_pointer)
255 def remove (self, sfa_record):
256 type = sfa_record['type']
257 pointer = sfa_record['pointer']
259 persons = self.shell.GetPersons({'peer_id': None, 'person_id': pointer})
260 # only delete this person if he has site ids. if he doesnt, it probably means
261 # he was just removed from a site, not actually deleted
262 if persons and persons[0]['site_ids']:
263 self.shell.DeletePerson(pointer)
264 elif type == 'slice':
265 if self.shell.GetSlices({'peer_id': None, 'slice_id': pointer}):
266 self.shell.DeleteSlice(pointer)
268 if self.shell.GetNodes({'peer_id': None, 'node_id': pointer}):
269 self.shell.DeleteNode(pointer)
270 elif type == 'authority':
271 if self.shell.GetSites({'peer_id': None, 'site_id': pointer}):
272 self.shell.DeleteSite(pointer)
278 # Convert SFA fields to PLC fields for use when registering or updating
279 # registry record in the PLC database
282 def sfa_fields_to_pl_fields(self, type, hrn, sfa_record):
287 pl_record["name"] = hrn_to_pl_slicename(hrn)
288 if "instantiation" in sfa_record:
289 pl_record['instantiation'] = sfa_record['instantiation']
291 pl_record["instantiation"] = "plc-instantiated"
292 if "url" in sfa_record:
293 pl_record["url"] = sfa_record["url"]
294 if "description" in sfa_record:
295 pl_record["description"] = sfa_record["description"]
296 if "expires" in sfa_record:
297 date = utcparse(sfa_record['expires'])
298 expires = datetime_to_epoch(date)
299 pl_record["expires"] = expires
302 if not "hostname" in pl_record:
303 # fetch from sfa_record
304 if "hostname" not in sfa_record:
305 raise MissingSfaInfo("hostname")
306 pl_record["hostname"] = sfa_record["hostname"]
307 if "model" in sfa_record:
308 pl_record["model"] = sfa_record["model"]
310 pl_record["model"] = "geni"
312 elif type == "authority":
313 pl_record["login_base"] = PlXrn(xrn=hrn,type='authority').pl_login_base()
314 if "name" not in sfa_record or not sfa_record['name']:
315 pl_record["name"] = hrn
316 if "abbreviated_name" not in sfa_record:
317 pl_record["abbreviated_name"] = hrn
318 if "enabled" not in sfa_record:
319 pl_record["enabled"] = True
320 if "is_public" not in sfa_record:
321 pl_record["is_public"] = True
326 def fill_record_info(self, records):
328 Given a (list of) SFA record, fill in the PLC specific
329 and SFA specific fields in the record.
331 if not isinstance(records, list):
334 self.fill_record_pl_info(records)
335 self.fill_record_hrns(records)
336 self.fill_record_sfa_info(records)
339 def fill_record_pl_info(self, records):
341 Fill in the planetlab specific fields of a SFA record. This
342 involves calling the appropriate PLC method to retrieve the
343 database record for the object.
345 @param record: record to fill in field (in/out param)
348 node_ids, site_ids, slice_ids = [], [], []
349 person_ids, key_ids = [], []
350 type_map = {'node': node_ids, 'authority': site_ids,
351 'slice': slice_ids, 'user': person_ids}
353 for record in records:
354 for type in type_map:
355 if type == record['type']:
356 type_map[type].append(record['pointer'])
359 nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
361 node_list = self.shell.GetNodes({'peer_id': None, 'node_id': node_ids})
362 nodes = list_to_dict(node_list, 'node_id')
364 site_list = self.shell.GetSites({'peer_id': None, 'site_id': site_ids})
365 sites = list_to_dict(site_list, 'site_id')
367 slice_list = self.shell.GetSlices({'peer_id': None, 'slice_id': slice_ids})
368 slices = list_to_dict(slice_list, 'slice_id')
370 person_list = self.shell.GetPersons({'peer_id': None, 'person_id': person_ids})
371 persons = list_to_dict(person_list, 'person_id')
372 for person in persons:
373 key_ids.extend(persons[person]['key_ids'])
375 pl_records = {'node': nodes, 'authority': sites,
376 'slice': slices, 'user': persons}
379 key_list = self.shell.GetKeys(key_ids)
380 keys = list_to_dict(key_list, 'key_id')
383 for record in records:
384 # records with pointer==-1 do not have plc info.
385 # for example, the top level authority records which are
386 # authorities, but not PL "sites"
387 if record['pointer'] == -1:
390 for type in pl_records:
391 if record['type'] == type:
392 if record['pointer'] in pl_records[type]:
393 record.update(pl_records[type][record['pointer']])
396 if record['type'] == 'user':
397 if 'key_ids' not in record:
398 logger.info("user record has no 'key_ids' - need to import from myplc ?")
400 pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys]
401 record['keys'] = pubkeys
405 def fill_record_hrns(self, records):
407 convert pl ids to hrns
411 slice_ids, person_ids, site_ids, node_ids = [], [], [], []
412 for record in records:
413 if 'site_id' in record:
414 site_ids.append(record['site_id'])
415 if 'site_ids' in record:
416 site_ids.extend(record['site_ids'])
417 if 'person_ids' in record:
418 person_ids.extend(record['person_ids'])
419 if 'slice_ids' in record:
420 slice_ids.extend(record['slice_ids'])
421 if 'node_ids' in record:
422 node_ids.extend(record['node_ids'])
425 slices, persons, sites, nodes = {}, {}, {}, {}
427 site_list = self.shell.GetSites({'peer_id': None, 'site_id': site_ids}, ['site_id', 'login_base'])
428 sites = list_to_dict(site_list, 'site_id')
430 person_list = self.shell.GetPersons({'peer_id': None, 'person_id': person_ids}, ['person_id', 'email'])
431 persons = list_to_dict(person_list, 'person_id')
433 slice_list = self.shell.GetSlices({'peer_id': None, 'slice_id': slice_ids}, ['slice_id', 'name'])
434 slices = list_to_dict(slice_list, 'slice_id')
436 node_list = self.shell.GetNodes({'peer_id': None, 'node_id': node_ids}, ['node_id', 'hostname'])
437 nodes = list_to_dict(node_list, 'node_id')
439 # convert ids to hrns
440 for record in records:
441 # get all relevant data
442 type = record['type']
443 pointer = record['pointer']
449 if 'site_id' in record:
450 site = sites[record['site_id']]
451 login_base = site['login_base']
452 record['site'] = ".".join([auth_hrn, login_base])
453 if 'person_ids' in record:
454 emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
455 if person_id in persons]
456 usernames = [email.split('@')[0] for email in emails]
457 person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
458 record['persons'] = person_hrns
459 if 'slice_ids' in record:
460 slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
461 if slice_id in slices]
462 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
463 record['slices'] = slice_hrns
464 if 'node_ids' in record:
465 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
467 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
468 record['nodes'] = node_hrns
469 if 'site_ids' in record:
470 login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
472 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
473 record['sites'] = site_hrns
475 if 'expires' in record:
476 date = utcparse(record['expires'])
477 datestring = datetime_to_string(date)
478 record['expires'] = datestring
482 def fill_record_sfa_info(self, records):
484 def startswith(prefix, values):
485 return [value for value in values if value.startswith(prefix)]
490 for record in records:
491 person_ids.extend(record.get("person_ids", []))
492 site_ids.extend(record.get("site_ids", []))
493 if 'site_id' in record:
494 site_ids.append(record['site_id'])
496 # get all pis from the sites we've encountered
497 # and store them in a dictionary keyed on site_id
500 pi_filter = {'peer_id': None, '|roles': ['pi'], '|site_ids': site_ids}
501 pi_list = self.shell.GetPersons(pi_filter, ['person_id', 'site_ids'])
503 # we will need the pi's hrns also
504 person_ids.append(pi['person_id'])
506 # we also need to keep track of the sites these pis
508 for site_id in pi['site_ids']:
509 if site_id in site_pis:
510 site_pis[site_id].append(pi)
512 site_pis[site_id] = [pi]
514 # get sfa records for all records associated with these records.
515 # we'll replace pl ids (person_ids) with hrns from the sfa records
518 # get the registry records
519 person_list, persons = [], {}
520 person_list = self.api.dbsession().query (RegRecord).filter(RegRecord.pointer.in_(person_ids))
521 # create a hrns keyed on the sfa record's pointer.
522 # Its possible for multiple records to have the same pointer so
523 # the dict's value will be a list of hrns.
524 persons = defaultdict(list)
525 for person in person_list:
526 persons[person.pointer].append(person)
529 pl_person_list, pl_persons = [], {}
530 pl_person_list = self.shell.GetPersons(person_ids, ['person_id', 'roles'])
531 pl_persons = list_to_dict(pl_person_list, 'person_id')
534 for record in records:
535 # skip records with no pl info (top level authorities)
536 #if record['pointer'] == -1:
539 type = record['type']
540 logger.info("fill_record_sfa_info - incoming record typed {}".format(type))
541 if (type == "slice"):
542 # all slice users are researchers
543 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
545 record['researcher'] = []
546 for person_id in record.get('person_ids', []):
547 hrns = [person.hrn for person in persons[person_id]]
548 record['researcher'].extend(hrns)
550 # pis at the slice's site
551 if 'site_id' in record and record['site_id'] in site_pis:
552 pl_pis = site_pis[record['site_id']]
553 pi_ids = [pi['person_id'] for pi in pl_pis]
554 for person_id in pi_ids:
555 hrns = [person.hrn for person in persons[person_id]]
556 record['PI'].extend(hrns)
557 record['geni_creator'] = record['PI']
559 elif (type.startswith("authority")):
561 logger.info("fill_record_sfa_info - authority xherex")
562 if record['pointer'] != -1:
564 record['operator'] = []
566 for pointer in record.get('person_ids', []):
567 if pointer not in persons or pointer not in pl_persons:
568 # this means there is not sfa or pl record for this user
570 hrns = [person.hrn for person in persons[pointer]]
571 roles = pl_persons[pointer]['roles']
573 record['PI'].extend(hrns)
575 record['operator'].extend(hrns)
577 record['owner'].extend(hrns)
578 # xxx TODO: OrganizationName
579 elif (type == "node"):
580 sfa_info['dns'] = record.get("hostname", "")
581 # xxx TODO: URI, LatLong, IP, DNS
583 elif (type == "user"):
584 logger.info('setting user.email')
585 sfa_info['email'] = record.get("email", "")
586 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
587 sfa_info['geni_certificate'] = record['gid']
588 # xxx TODO: PostalAddress, Phone
589 record.update(sfa_info)
593 # plcapi works by changes, compute what needs to be added/deleted
594 def update_relation (self, subject_type, target_type, relation_name, subject_id, target_ids):
595 # hard-wire the code for slice/user for now, could be smarter if needed
596 if subject_type == 'slice' and target_type == 'user' and relation_name == 'researcher':
597 subject = self.shell.GetSlices (subject_id)[0]
598 current_target_ids = subject['person_ids']
599 add_target_ids = list ( set (target_ids).difference(current_target_ids))
600 del_target_ids = list ( set (current_target_ids).difference(target_ids))
601 logger.debug ("subject_id = {} (type={})".format(subject_id, type(subject_id)))
602 for target_id in add_target_ids:
603 self.shell.AddPersonToSlice (target_id,subject_id)
604 logger.debug ("add_target_id = {} (type={})".format(target_id, type(target_id)))
605 for target_id in del_target_ids:
606 logger.debug ("del_target_id = {} (type={})".format(target_id, type(target_id)))
607 self.shell.DeletePersonFromSlice (target_id, subject_id)
608 elif subject_type == 'authority' and target_type == 'user' and relation_name == 'pi':
609 # due to the plcapi limitations this means essentially adding pi role to all people in the list
610 # it's tricky to remove any pi role here, although it might be desirable
611 persons = self.shell.GetPersons ({'peer_id': None, 'person_id': target_ids})
612 for person in persons:
613 if 'pi' not in person['roles']:
614 self.shell.AddRoleToPerson('pi',person['person_id'])
616 logger.info('unexpected relation {} to maintain, {} -> {}'\
617 .format(relation_name, subject_type, target_type))
620 ########################################
621 ########## aggregate oriented
622 ########################################
624 def testbed_name (self): return "myplc"
626 def aggregate_version (self):
629 # first 2 args are None in case of resource discovery
630 def list_resources (self, version=None, options=None):
631 if options is None: options={}
632 aggregate = PlAggregate(self)
633 rspec = aggregate.list_resources(version=version, options=options)
636 def describe(self, urns, version, options=None):
637 if options is None: options={}
638 aggregate = PlAggregate(self)
639 return aggregate.describe(urns, version=version, options=options)
641 def status (self, urns, options=None):
642 if options is None: options={}
643 aggregate = PlAggregate(self)
644 desc = aggregate.describe(urns, version='GENI 3')
645 status = {'geni_urn': desc['geni_urn'],
646 'geni_slivers': desc['geni_slivers']}
649 def allocate (self, urn, rspec_string, expiration, options=None):
655 (*) append : if set to True, provided attributes are appended
656 to the current list of tags for the slice
657 otherwise, the set of provided attributes are meant to be the
658 the exact set of tags at the end of the call, meaning pre-existing tags
659 are deleted if not repeated in the incoming request
661 if options is None: options={}
663 aggregate = PlAggregate(self)
664 slices = PlSlices(self)
665 sfa_peer = slices.get_sfa_peer(xrn.get_hrn())
667 users = options.get('geni_users', [])
670 slice_record = users[0].get('slice_record', {})
673 rspec = RSpec(rspec_string)
674 requested_attributes = rspec.version.get_slice_attributes()
676 # ensure site record exists
677 site = slices.verify_site(xrn.hrn, slice_record, sfa_peer, options=options)
678 # ensure slice record exists
679 slice = slices.verify_slice(xrn.hrn, slice_record, sfa_peer, expiration=expiration, options=options)
680 # ensure person records exists
681 persons = slices.verify_persons(xrn.hrn, slice, users, sfa_peer, options=options)
682 # ensure slice attributes exists
683 slices.verify_slice_tags(slice, requested_attributes, options=options)
685 # add/remove slice from nodes
686 request_nodes = rspec.version.get_nodes_with_slivers()
687 nodes = slices.verify_slice_nodes(urn, slice, request_nodes)
689 # add/remove links links
690 slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
693 rspec_requested_leases = rspec.version.get_leases()
694 leases = slices.verify_slice_leases(slice, rspec_requested_leases)
696 return aggregate.describe([xrn.get_urn()], version=rspec.version)
698 def provision(self, urns, options=None):
699 if options is None: options={}
701 slices = PlSlices(self)
702 aggregate = PlAggregate(self)
703 slivers = aggregate.get_slivers(urns)
705 sliver_id_parts = Xrn(urns[0]).get_sliver_id_parts()
706 # allow to be called with an empty rspec, meaning flush reservations
710 filter['slice_id'] = int(sliver_id_parts[0])
712 filter['name'] = sliver_id_parts[0]
713 slices = self.shell.GetSlices(filter,['hrn'])
715 raise Forbidden("Unable to locate slice record for sliver: {}".format(xrn))
717 slice_urn = hrn_to_urn(slice['hrn'], type='slice')
720 slice_id = slivers[0]['slice_id']
721 slice_hrn = self.shell.GetSliceHrn(slice_id)
722 slice = self.shell.GetSlices({'slice_id': slice_id})[0]
723 slice['hrn'] = slice_hrn
724 sfa_peer = slices.get_sfa_peer(slice['hrn'])
725 users = options.get('geni_users', [])
726 persons = slices.verify_persons(slice['hrn'], slice, users, sfa_peer, options=options)
727 # update sliver allocation states and set them to geni_provisioned
728 sliver_ids = [sliver['sliver_id'] for sliver in slivers]
729 dbsession = self.api.dbsession()
730 SliverAllocation.set_allocations(sliver_ids, 'geni_provisioned',dbsession)
732 version_manager = VersionManager()
733 rspec_version = version_manager.get_version(options['geni_rspec_version'])
734 return self.describe(urns, rspec_version, options=options)
736 def delete(self, urns, options=None):
737 if options is None: options={}
738 # collect sliver ids so we can update sliver allocation states after
739 # we remove the slivers.
740 aggregate = PlAggregate(self)
741 slivers = aggregate.get_slivers(urns)
743 slice_id = slivers[0]['slice_id']
744 slice_name = slivers[0]['name']
747 for sliver in slivers:
748 node_ids.append(sliver['node_id'])
749 sliver_ids.append(sliver['sliver_id'])
752 leases = self.shell.GetLeases({'name': slice_name, 'node_id': node_ids})
753 leases_ids = [lease['lease_id'] for lease in leases ]
755 slice_hrn = self.shell.GetSliceHrn(int(slice_id))
757 self.shell.DeleteSliceFromNodes(slice_id, node_ids)
758 if len(leases_ids) > 0:
759 self.shell.DeleteLeases(leases_ids)
761 # delete sliver allocation states
762 dbsession = self.api.dbsession()
763 SliverAllocation.delete_allocations(sliver_ids, dbsession)
767 # prepare return struct
769 for sliver in slivers:
771 {'geni_sliver_urn': sliver['sliver_id'],
772 'geni_allocation_status': 'geni_unallocated',
773 'geni_expires': datetime_to_string(utcparse(sliver['expires']))})
776 def renew (self, urns, expiration_time, options=None):
777 if options is None: options={}
778 aggregate = PlAggregate(self)
779 slivers = aggregate.get_slivers(urns)
781 raise SearchFailed(urns)
783 requested_time = utcparse(expiration_time)
784 record = {'expires': int(datetime_to_epoch(requested_time))}
785 self.shell.UpdateSlice(slice['slice_id'], record)
786 description = self.describe(urns, 'GENI 3', options)
787 return description['geni_slivers']
790 def perform_operational_action (self, urns, action, options=None):
791 if options is None: options={}
792 # MyPLC doesn't support operational actions. Lets pretend like it
793 # supports start, but reject everything else.
794 action = action.lower()
795 if action not in ['geni_start']:
796 raise UnsupportedOperation(action)
798 # fault if sliver is not full allocated (operational status is geni_pending_allocation)
799 description = self.describe(urns, 'GENI 3', options)
800 for sliver in description['geni_slivers']:
801 if sliver['geni_operational_status'] == 'geni_pending_allocation':
802 raise UnsupportedOperation\
803 (action, "Sliver must be fully allocated (operational status is not geni_pending_allocation)")
805 # Perform Operational Action Here
808 geni_slivers = self.describe(urns, 'GENI 3', options)['geni_slivers']
811 # set the 'enabled' tag to 0
812 def shutdown (self, xrn, options=None):
813 if options is None: options={}
814 hrn, _ = urn_to_hrn(xrn)
815 top_auth_hrn = top_auth(hrn)
816 site_hrn = '.'.join(hrn.split('.')[:-1])
817 slice_part = hrn.split('.')[-1]
818 if top_auth_hrn == self.hrn:
819 login_base = slice_hrn.split('.')[-2][:12]
821 login_base = hash_loginbase(site_hrn)
823 slicename = '_'.join([login_base, slice_part])
825 slices = self.shell.GetSlices({'peer_id': None, 'name': slicename}, ['slice_id'])
827 raise RecordNotFound(slice_hrn)
828 slice_id = slices[0]['slice_id']
829 slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'})
831 self.shell.AddSliceTag(slice_id, 'enabled', '0')
832 elif slice_tags[0]['value'] != "0":
833 tag_id = slice_tags[0]['slice_tag_id']
834 self.shell.UpdateSliceTag(tag_id, '0')