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: sfa_record[key]='*from*sfa*'
151 # AddPerson does not allow everything to be set
152 can_add = ['first_name', 'last_name', 'title','email', 'password', 'phone', 'url', 'bio']
153 add_person_dict = { k : sfa_record[k] for k in sfa_record if k in can_add }
154 pointer = self.shell.AddPerson(add_person_dict)
155 self.shell.SetPersonHrn(int(pointer), hrn)
157 pointer = persons[0]['person_id']
159 # enable the person's account
160 self.shell.UpdatePerson(pointer, {'enabled': True})
161 # add this person to the site
162 login_base = get_leaf(sfa_record['authority'])
163 self.shell.AddPersonToSite(pointer, login_base)
165 # What roles should this user have?
167 if 'roles' in sfa_record:
168 # if specified in xml, but only low-level roles
169 roles = [ role for role in sfa_record['roles'] if role in ['user','tech'] ]
170 # at least user if no other cluse could be found
174 self.shell.AddRoleToPerson(role, pointer)
177 self.shell.AddPersonKey(pointer, {'key_type' : 'ssh', 'key' : pub_key})
180 login_base = PlXrn(xrn=sfa_record['authority'], type='authority').pl_login_base()
181 nodes = self.shell.GetNodes({'peer_id': None, 'hostname': pl_record['hostname']})
183 pointer = self.shell.AddNode(login_base, pl_record)
184 self.shell.SetNodeHrn(int(pointer), hrn)
186 pointer = nodes[0]['node_id']
191 # xxx actually old_sfa_record comes filled with plc stuff as well in the original code
192 def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
193 pointer = old_sfa_record['pointer']
194 type = old_sfa_record['type']
195 new_key_pointer = None
197 # new_key implemented for users only
198 if new_key and type not in [ 'user' ]:
199 raise UnknownSfaType(type)
201 if (type == "authority"):
202 logger.debug("pldriver.update: calling UpdateSite with {}".format(new_sfa_record))
203 self.shell.UpdateSite(pointer, new_sfa_record)
204 self.shell.SetSiteHrn(pointer, hrn)
206 elif type == "slice":
207 pl_record=self.sfa_fields_to_pl_fields(type, hrn, new_sfa_record)
208 if 'name' in pl_record:
209 pl_record.pop('name')
210 self.shell.UpdateSlice(pointer, pl_record)
211 self.shell.SetSliceHrn(pointer, hrn)
214 # SMBAKER: UpdatePerson only allows a limited set of fields to be
215 # updated. Ideally we should have a more generic way of doing
216 # this. I copied the field names from UpdatePerson.py...
218 all_fields = new_sfa_record
219 for key in all_fields.keys():
220 if key in ['first_name', 'last_name', 'title', 'email',
221 'password', 'phone', 'url', 'bio', 'accepted_aup',
223 update_fields[key] = all_fields[key]
224 # when updating a user, we always get a 'email' field at this point
225 # this is because 'email' is a native field in the RegUser object...
226 if 'email' in update_fields and not update_fields['email']:
227 del update_fields['email']
228 self.shell.UpdatePerson(pointer, update_fields)
229 self.shell.SetPersonHrn(pointer, hrn)
232 # must check this key against the previous one if it exists
233 persons = self.shell.GetPersons({'peer_id': None, 'person_id': pointer}, ['key_ids'])
235 keys = person['key_ids']
236 keys = self.shell.GetKeys(person['key_ids'])
240 if new_key == key['key']:
242 new_key_pointer = key['key_id']
245 new_key_pointer = self.shell.AddPersonKey(pointer, {'key_type': 'ssh', 'key': new_key})
248 self.shell.UpdateNode(pointer, new_sfa_record)
250 return (pointer, new_key_pointer)
254 def remove (self, sfa_record):
255 type=sfa_record['type']
256 pointer=sfa_record['pointer']
258 persons = self.shell.GetPersons({'peer_id': None, 'person_id': pointer})
259 # only delete this person if he has site ids. if he doesnt, it probably means
260 # he was just removed from a site, not actually deleted
261 if persons and persons[0]['site_ids']:
262 self.shell.DeletePerson(pointer)
263 elif type == 'slice':
264 if self.shell.GetSlices({'peer_id': None, 'slice_id': pointer}):
265 self.shell.DeleteSlice(pointer)
267 if self.shell.GetNodes({'peer_id': None, 'node_id': pointer}):
268 self.shell.DeleteNode(pointer)
269 elif type == 'authority':
270 if self.shell.GetSites({'peer_id': None, 'site_id': pointer}):
271 self.shell.DeleteSite(pointer)
277 # Convert SFA fields to PLC fields for use when registering or updating
278 # registry record in the PLC database
281 def sfa_fields_to_pl_fields(self, type, hrn, sfa_record):
286 pl_record["name"] = hrn_to_pl_slicename(hrn)
287 if "instantiation" in sfa_record:
288 pl_record['instantiation']=sfa_record['instantiation']
290 pl_record["instantiation"] = "plc-instantiated"
291 if "url" in sfa_record:
292 pl_record["url"] = sfa_record["url"]
293 if "description" in sfa_record:
294 pl_record["description"] = sfa_record["description"]
295 if "expires" in sfa_record:
296 date = utcparse(sfa_record['expires'])
297 expires = datetime_to_epoch(date)
298 pl_record["expires"] = expires
301 if not "hostname" in pl_record:
302 # fetch from sfa_record
303 if "hostname" not in sfa_record:
304 raise MissingSfaInfo("hostname")
305 pl_record["hostname"] = sfa_record["hostname"]
306 if "model" in sfa_record:
307 pl_record["model"] = sfa_record["model"]
309 pl_record["model"] = "geni"
311 elif type == "authority":
312 pl_record["login_base"] = PlXrn(xrn=hrn,type='authority').pl_login_base()
313 if "name" not in sfa_record or not sfa_record['name']:
314 pl_record["name"] = hrn
315 if "abbreviated_name" not in sfa_record:
316 pl_record["abbreviated_name"] = hrn
317 if "enabled" not in sfa_record:
318 pl_record["enabled"] = True
319 if "is_public" not in sfa_record:
320 pl_record["is_public"] = True
325 def fill_record_info(self, records):
327 Given a (list of) SFA record, fill in the PLC specific
328 and SFA specific fields in the record.
330 if not isinstance(records, list):
333 self.fill_record_pl_info(records)
334 self.fill_record_hrns(records)
335 self.fill_record_sfa_info(records)
338 def fill_record_pl_info(self, records):
340 Fill in the planetlab specific fields of a SFA record. This
341 involves calling the appropriate PLC method to retrieve the
342 database record for the object.
344 @param record: record to fill in field (in/out param)
347 node_ids, site_ids, slice_ids = [], [], []
348 person_ids, key_ids = [], []
349 type_map = {'node': node_ids, 'authority': site_ids,
350 'slice': slice_ids, 'user': person_ids}
352 for record in records:
353 for type in type_map:
354 if type == record['type']:
355 type_map[type].append(record['pointer'])
358 nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
360 node_list = self.shell.GetNodes({'peer_id': None, 'node_id': node_ids})
361 nodes = list_to_dict(node_list, 'node_id')
363 site_list = self.shell.GetSites({'peer_id': None, 'site_id': site_ids})
364 sites = list_to_dict(site_list, 'site_id')
366 slice_list = self.shell.GetSlices({'peer_id': None, 'slice_id': slice_ids})
367 slices = list_to_dict(slice_list, 'slice_id')
369 person_list = self.shell.GetPersons({'peer_id': None, 'person_id': person_ids})
370 persons = list_to_dict(person_list, 'person_id')
371 for person in persons:
372 key_ids.extend(persons[person]['key_ids'])
374 pl_records = {'node': nodes, 'authority': sites,
375 'slice': slices, 'user': persons}
378 key_list = self.shell.GetKeys(key_ids)
379 keys = list_to_dict(key_list, 'key_id')
382 for record in records:
383 # records with pointer==-1 do not have plc info.
384 # for example, the top level authority records which are
385 # authorities, but not PL "sites"
386 if record['pointer'] == -1:
389 for type in pl_records:
390 if record['type'] == type:
391 if record['pointer'] in pl_records[type]:
392 record.update(pl_records[type][record['pointer']])
395 if record['type'] == 'user':
396 if 'key_ids' not in record:
397 logger.info("user record has no 'key_ids' - need to import from myplc ?")
399 pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys]
400 record['keys'] = pubkeys
404 def fill_record_hrns(self, records):
406 convert pl ids to hrns
410 slice_ids, person_ids, site_ids, node_ids = [], [], [], []
411 for record in records:
412 if 'site_id' in record:
413 site_ids.append(record['site_id'])
414 if 'site_ids' in record:
415 site_ids.extend(record['site_ids'])
416 if 'person_ids' in record:
417 person_ids.extend(record['person_ids'])
418 if 'slice_ids' in record:
419 slice_ids.extend(record['slice_ids'])
420 if 'node_ids' in record:
421 node_ids.extend(record['node_ids'])
424 slices, persons, sites, nodes = {}, {}, {}, {}
426 site_list = self.shell.GetSites({'peer_id': None, 'site_id': site_ids}, ['site_id', 'login_base'])
427 sites = list_to_dict(site_list, 'site_id')
429 person_list = self.shell.GetPersons({'peer_id': None, 'person_id': person_ids}, ['person_id', 'email'])
430 persons = list_to_dict(person_list, 'person_id')
432 slice_list = self.shell.GetSlices({'peer_id': None, 'slice_id': slice_ids}, ['slice_id', 'name'])
433 slices = list_to_dict(slice_list, 'slice_id')
435 node_list = self.shell.GetNodes({'peer_id': None, 'node_id': node_ids}, ['node_id', 'hostname'])
436 nodes = list_to_dict(node_list, 'node_id')
438 # convert ids to hrns
439 for record in records:
440 # get all relevant data
441 type = record['type']
442 pointer = record['pointer']
448 if 'site_id' in record:
449 site = sites[record['site_id']]
450 login_base = site['login_base']
451 record['site'] = ".".join([auth_hrn, login_base])
452 if 'person_ids' in record:
453 emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
454 if person_id in persons]
455 usernames = [email.split('@')[0] for email in emails]
456 person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
457 record['persons'] = person_hrns
458 if 'slice_ids' in record:
459 slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
460 if slice_id in slices]
461 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
462 record['slices'] = slice_hrns
463 if 'node_ids' in record:
464 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
466 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
467 record['nodes'] = node_hrns
468 if 'site_ids' in record:
469 login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
471 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
472 record['sites'] = site_hrns
474 if 'expires' in record:
475 date = utcparse(record['expires'])
476 datestring = datetime_to_string(date)
477 record['expires'] = datestring
481 def fill_record_sfa_info(self, records):
483 def startswith(prefix, values):
484 return [value for value in values if value.startswith(prefix)]
489 for record in records:
490 person_ids.extend(record.get("person_ids", []))
491 site_ids.extend(record.get("site_ids", []))
492 if 'site_id' in record:
493 site_ids.append(record['site_id'])
495 # get all pis from the sites we've encountered
496 # and store them in a dictionary keyed on site_id
499 pi_filter = {'peer_id': None, '|roles': ['pi'], '|site_ids': site_ids}
500 pi_list = self.shell.GetPersons(pi_filter, ['person_id', 'site_ids'])
502 # we will need the pi's hrns also
503 person_ids.append(pi['person_id'])
505 # we also need to keep track of the sites these pis
507 for site_id in pi['site_ids']:
508 if site_id in site_pis:
509 site_pis[site_id].append(pi)
511 site_pis[site_id] = [pi]
513 # get sfa records for all records associated with these records.
514 # we'll replace pl ids (person_ids) with hrns from the sfa records
517 # get the registry records
518 person_list, persons = [], {}
519 person_list = self.api.dbsession().query (RegRecord).filter(RegRecord.pointer.in_(person_ids))
520 # create a hrns keyed on the sfa record's pointer.
521 # Its possible for multiple records to have the same pointer so
522 # the dict's value will be a list of hrns.
523 persons = defaultdict(list)
524 for person in person_list:
525 persons[person.pointer].append(person)
528 pl_person_list, pl_persons = [], {}
529 pl_person_list = self.shell.GetPersons(person_ids, ['person_id', 'roles'])
530 pl_persons = list_to_dict(pl_person_list, 'person_id')
533 for record in records:
534 # skip records with no pl info (top level authorities)
535 #if record['pointer'] == -1:
538 type = record['type']
539 logger.info("fill_record_sfa_info - incoming record typed {}".format(type))
540 if (type == "slice"):
541 # all slice users are researchers
542 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
544 record['researcher'] = []
545 for person_id in record.get('person_ids', []):
546 hrns = [person.hrn for person in persons[person_id]]
547 record['researcher'].extend(hrns)
549 # pis at the slice's site
550 if 'site_id' in record and record['site_id'] in site_pis:
551 pl_pis = site_pis[record['site_id']]
552 pi_ids = [pi['person_id'] for pi in pl_pis]
553 for person_id in pi_ids:
554 hrns = [person.hrn for person in persons[person_id]]
555 record['PI'].extend(hrns)
556 record['geni_creator'] = record['PI']
558 elif (type.startswith("authority")):
560 logger.info("fill_record_sfa_info - authority xherex")
561 if record['pointer'] != -1:
563 record['operator'] = []
565 for pointer in record.get('person_ids', []):
566 if pointer not in persons or pointer not in pl_persons:
567 # this means there is not sfa or pl record for this user
569 hrns = [person.hrn for person in persons[pointer]]
570 roles = pl_persons[pointer]['roles']
572 record['PI'].extend(hrns)
574 record['operator'].extend(hrns)
576 record['owner'].extend(hrns)
577 # xxx TODO: OrganizationName
578 elif (type == "node"):
579 sfa_info['dns'] = record.get("hostname", "")
580 # xxx TODO: URI, LatLong, IP, DNS
582 elif (type == "user"):
583 logger.info('setting user.email')
584 sfa_info['email'] = record.get("email", "")
585 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
586 sfa_info['geni_certificate'] = record['gid']
587 # xxx TODO: PostalAddress, Phone
588 record.update(sfa_info)
592 # plcapi works by changes, compute what needs to be added/deleted
593 def update_relation (self, subject_type, target_type, relation_name, subject_id, target_ids):
594 # hard-wire the code for slice/user for now, could be smarter if needed
595 if subject_type =='slice' and target_type == 'user' and relation_name == 'researcher':
596 subject=self.shell.GetSlices (subject_id)[0]
597 current_target_ids = subject['person_ids']
598 add_target_ids = list ( set (target_ids).difference(current_target_ids))
599 del_target_ids = list ( set (current_target_ids).difference(target_ids))
600 logger.debug ("subject_id = {} (type={})".format(subject_id, type(subject_id)))
601 for target_id in add_target_ids:
602 self.shell.AddPersonToSlice (target_id,subject_id)
603 logger.debug ("add_target_id = {} (type={})".format(target_id, type(target_id)))
604 for target_id in del_target_ids:
605 logger.debug ("del_target_id = {} (type={})".format(target_id, type(target_id)))
606 self.shell.DeletePersonFromSlice (target_id, subject_id)
607 elif subject_type == 'authority' and target_type == 'user' and relation_name == 'pi':
608 # due to the plcapi limitations this means essentially adding pi role to all people in the list
609 # it's tricky to remove any pi role here, although it might be desirable
610 persons = self.shell.GetPersons ({'peer_id': None, 'person_id': target_ids})
611 for person in persons:
612 if 'pi' not in person['roles']:
613 self.shell.AddRoleToPerson('pi',person['person_id'])
615 logger.info('unexpected relation {} to maintain, {} -> {}'\
616 .format(relation_name, subject_type, target_type))
619 ########################################
620 ########## aggregate oriented
621 ########################################
623 def testbed_name (self): return "myplc"
625 def aggregate_version (self):
628 # first 2 args are None in case of resource discovery
629 def list_resources (self, version=None, options=None):
630 if options is None: options={}
631 aggregate = PlAggregate(self)
632 rspec = aggregate.list_resources(version=version, options=options)
635 def describe(self, urns, version, options=None):
636 if options is None: options={}
637 aggregate = PlAggregate(self)
638 return aggregate.describe(urns, version=version, options=options)
640 def status (self, urns, options=None):
641 if options is None: options={}
642 aggregate = PlAggregate(self)
643 desc = aggregate.describe(urns, version='GENI 3')
644 status = {'geni_urn': desc['geni_urn'],
645 'geni_slivers': desc['geni_slivers']}
648 def allocate (self, urn, rspec_string, expiration, options=None):
654 (*) append : if set to True, provided attributes are appended
655 to the current list of tags for the slice
656 otherwise, the set of provided attributes are meant to be the
657 the exact set of tags at the end of the call, meaning pre-existing tags
658 are deleted if not repeated in the incoming request
660 if options is None: options={}
662 aggregate = PlAggregate(self)
663 slices = PlSlices(self)
664 sfa_peer = slices.get_sfa_peer(xrn.get_hrn())
666 users = options.get('geni_users', [])
669 slice_record = users[0].get('slice_record', {})
672 rspec = RSpec(rspec_string)
673 requested_attributes = rspec.version.get_slice_attributes()
675 # ensure site record exists
676 site = slices.verify_site(xrn.hrn, slice_record, sfa_peer, options=options)
677 # ensure slice record exists
678 slice = slices.verify_slice(xrn.hrn, slice_record, sfa_peer, expiration=expiration, options=options)
679 # ensure person records exists
680 persons = slices.verify_persons(xrn.hrn, slice, users, sfa_peer, options=options)
681 # ensure slice attributes exists
682 slices.verify_slice_attributes(slice, requested_attributes, options=options)
684 # add/remove slice from nodes
685 request_nodes = rspec.version.get_nodes_with_slivers()
686 nodes = slices.verify_slice_nodes(urn, slice, request_nodes)
688 # add/remove links links
689 slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
692 rspec_requested_leases = rspec.version.get_leases()
693 leases = slices.verify_slice_leases(slice, rspec_requested_leases)
695 return aggregate.describe([xrn.get_urn()], version=rspec.version)
697 def provision(self, urns, options=None):
698 if options is None: options={}
700 slices = PlSlices(self)
701 aggregate = PlAggregate(self)
702 slivers = aggregate.get_slivers(urns)
704 sliver_id_parts = Xrn(urns[0]).get_sliver_id_parts()
705 # allow to be called with an empty rspec, meaning flush reservations
709 filter['slice_id'] = int(sliver_id_parts[0])
711 filter['name'] = sliver_id_parts[0]
712 slices = self.shell.GetSlices(filter,['hrn'])
714 raise Forbidden("Unable to locate slice record for sliver: {}".format(xrn))
716 slice_urn = hrn_to_urn(slice['hrn'], type='slice')
719 slice_id = slivers[0]['slice_id']
720 slice_hrn = self.shell.GetSliceHrn(slice_id)
721 slice = self.shell.GetSlices({'slice_id': slice_id})[0]
722 slice['hrn'] = slice_hrn
723 sfa_peer = slices.get_sfa_peer(slice['hrn'])
724 users = options.get('geni_users', [])
725 persons = slices.verify_persons(slice['hrn'], slice, users, sfa_peer, options=options)
726 # update sliver allocation states and set them to geni_provisioned
727 sliver_ids = [sliver['sliver_id'] for sliver in slivers]
728 dbsession=self.api.dbsession()
729 SliverAllocation.set_allocations(sliver_ids, 'geni_provisioned',dbsession)
731 version_manager = VersionManager()
732 rspec_version = version_manager.get_version(options['geni_rspec_version'])
733 return self.describe(urns, rspec_version, options=options)
735 def delete(self, urns, options=None):
736 if options is None: options={}
737 # collect sliver ids so we can update sliver allocation states after
738 # we remove the slivers.
739 aggregate = PlAggregate(self)
740 slivers = aggregate.get_slivers(urns)
742 slice_id = slivers[0]['slice_id']
743 slice_name = slivers[0]['name']
746 for sliver in slivers:
747 node_ids.append(sliver['node_id'])
748 sliver_ids.append(sliver['sliver_id'])
751 leases = self.shell.GetLeases({'name': slice_name, 'node_id': node_ids})
752 leases_ids = [lease['lease_id'] for lease in leases ]
754 slice_hrn = self.shell.GetSliceHrn(int(slice_id))
756 self.shell.DeleteSliceFromNodes(slice_id, node_ids)
757 if len(leases_ids) > 0:
758 self.shell.DeleteLeases(leases_ids)
760 # delete sliver allocation states
761 dbsession = self.api.dbsession()
762 SliverAllocation.delete_allocations(sliver_ids, dbsession)
766 # prepare return struct
768 for sliver in slivers:
770 {'geni_sliver_urn': sliver['sliver_id'],
771 'geni_allocation_status': 'geni_unallocated',
772 'geni_expires': datetime_to_string(utcparse(sliver['expires']))})
775 def renew (self, urns, expiration_time, options=None):
776 if options is None: options={}
777 aggregate = PlAggregate(self)
778 slivers = aggregate.get_slivers(urns)
780 raise SearchFailed(urns)
782 requested_time = utcparse(expiration_time)
783 record = {'expires': int(datetime_to_epoch(requested_time))}
784 self.shell.UpdateSlice(slice['slice_id'], record)
785 description = self.describe(urns, 'GENI 3', options)
786 return description['geni_slivers']
789 def perform_operational_action (self, urns, action, options=None):
790 if options is None: options={}
791 # MyPLC doesn't support operational actions. Lets pretend like it
792 # supports start, but reject everything else.
793 action = action.lower()
794 if action not in ['geni_start']:
795 raise UnsupportedOperation(action)
797 # fault if sliver is not full allocated (operational status is geni_pending_allocation)
798 description = self.describe(urns, 'GENI 3', options)
799 for sliver in description['geni_slivers']:
800 if sliver['geni_operational_status'] == 'geni_pending_allocation':
801 raise UnsupportedOperation\
802 (action, "Sliver must be fully allocated (operational status is not geni_pending_allocation)")
804 # Perform Operational Action Here
807 geni_slivers = self.describe(urns, 'GENI 3', options)['geni_slivers']
810 # set the 'enabled' tag to 0
811 def shutdown (self, xrn, options=None):
812 if options is None: options={}
813 hrn, _ = urn_to_hrn(xrn)
814 top_auth_hrn = top_auth(hrn)
815 site_hrn = '.'.join(hrn.split('.')[:-1])
816 slice_part = hrn.split('.')[-1]
817 if top_auth_hrn == self.hrn:
818 login_base = slice_hrn.split('.')[-2][:12]
820 login_base = hash_loginbase(site_hrn)
822 slicename = '_'.join([login_base, slice_part])
824 slices = self.shell.GetSlices({'peer_id': None, 'name': slicename}, ['slice_id'])
826 raise RecordNotFound(slice_hrn)
827 slice_id = slices[0]['slice_id']
828 slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'})
830 self.shell.AddSliceTag(slice_id, 'enabled', '0')
831 elif slice_tags[0]['value'] != "0":
832 tag_id = slice_tags[0]['slice_tag_id']
833 self.shell.UpdateSliceTag(tag_id, '0')