3 from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \
4 RecordNotFound, SfaNotImplemented, SliverDoesNotExist, SearchFailed
5 from sfa.util.sfalogging import logger
6 from sfa.util.defaultdict import defaultdict
7 from sfa.util.sfatime import utcparse, datetime_to_string, datetime_to_epoch
8 from sfa.util.xrn import hrn_to_urn, get_leaf
9 from sfa.util.cache import Cache
11 # one would think the driver should not need to mess with the SFA db, but..
12 from sfa.storage.alchemy import dbsession
13 from sfa.storage.model import RegRecord
15 # used to be used in get_ticket
16 #from sfa.trust.sfaticket import SfaTicket
17 from sfa.rspecs.version_manager import VersionManager
18 from sfa.rspecs.rspec import RSpec
20 # the driver interface, mostly provides default behaviours
21 from sfa.managers.driver import Driver
22 from sfa.planetlab.plshell import PlShell
23 import sfa.planetlab.peers as peers
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, xrn_to_hostname
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 dict ( [ (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, config):
47 Driver.__init__ (self, config)
48 self.shell = PlShell (config)
50 if config.SFA_AGGREGATE_CACHING:
51 if PlDriver.cache is None:
52 PlDriver.cache = Cache()
53 self.cache = PlDriver.cache
55 ########################################
56 ########## registry oriented
57 ########################################
59 def augment_records_with_testbed_info (self, sfa_records):
60 return self.fill_record_info (sfa_records)
63 def register (self, sfa_record, hrn, pub_key):
64 type = sfa_record['type']
65 pl_record = self.sfa_fields_to_pl_fields(type, hrn, sfa_record)
67 if type == 'authority':
68 sites = self.shell.GetSites([pl_record['login_base']])
70 # xxx when a site gets registered through SFA we need to set its max_slices
71 if 'max_slices' not in pl_record:
72 pl_record['max_slices']=2
73 pointer = self.shell.AddSite(pl_record)
75 pointer = sites[0]['site_id']
78 acceptable_fields=['url', 'instantiation', 'name', 'description']
79 for key in pl_record.keys():
80 if key not in acceptable_fields:
82 slices = self.shell.GetSlices([pl_record['name']])
84 pointer = self.shell.AddSlice(pl_record)
86 pointer = slices[0]['slice_id']
89 persons = self.shell.GetPersons({'email':sfa_record['email']})
91 for key in ['first_name','last_name']:
92 if key not in sfa_record: sfa_record[key]='*from*sfa*'
93 pointer = self.shell.AddPerson(dict(sfa_record))
95 pointer = persons[0]['person_id']
97 if 'enabled' in sfa_record and sfa_record['enabled']:
98 self.shell.UpdatePerson(pointer, {'enabled': sfa_record['enabled']})
99 # add this person to the site only if she is being added for the first
100 # time by sfa and doesont already exist in plc
101 if not persons or not persons[0]['site_ids']:
102 login_base = get_leaf(sfa_record['authority'])
103 self.shell.AddPersonToSite(pointer, login_base)
105 # What roles should this user have?
107 if 'roles' in sfa_record:
108 # if specified in xml, but only low-level roles
109 roles = [ role for role in sfa_record['roles'] if role in ['user','tech'] ]
110 # at least user if no other cluse could be found
114 self.shell.AddRoleToPerson(role, pointer)
117 self.shell.AddPersonKey(pointer, {'key_type' : 'ssh', 'key' : pub_key})
120 login_base = PlXrn(xrn=sfa_record['authority'],type='node').pl_login_base()
121 nodes = self.shell.GetNodes([pl_record['hostname']])
123 pointer = self.shell.AddNode(login_base, pl_record)
125 pointer = nodes[0]['node_id']
130 # xxx actually old_sfa_record comes filled with plc stuff as well in the original code
131 def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
132 pointer = old_sfa_record['pointer']
133 type = old_sfa_record['type']
135 # new_key implemented for users only
136 if new_key and type not in [ 'user' ]:
137 raise UnknownSfaType(type)
139 if (type == "authority"):
140 self.shell.UpdateSite(pointer, new_sfa_record)
142 elif type == "slice":
143 pl_record=self.sfa_fields_to_pl_fields(type, hrn, new_sfa_record)
144 if 'name' in pl_record:
145 pl_record.pop('name')
146 self.shell.UpdateSlice(pointer, pl_record)
149 # SMBAKER: UpdatePerson only allows a limited set of fields to be
150 # updated. Ideally we should have a more generic way of doing
151 # this. I copied the field names from UpdatePerson.py...
153 all_fields = new_sfa_record
154 for key in all_fields.keys():
155 if key in ['first_name', 'last_name', 'title', 'email',
156 'password', 'phone', 'url', 'bio', 'accepted_aup',
158 update_fields[key] = all_fields[key]
159 # when updating a user, we always get a 'email' field at this point
160 # this is because 'email' is a native field in the RegUser object...
161 if 'email' in update_fields and not update_fields['email']:
162 del update_fields['email']
163 self.shell.UpdatePerson(pointer, update_fields)
166 # must check this key against the previous one if it exists
167 persons = self.shell.GetPersons([pointer], ['key_ids'])
169 keys = person['key_ids']
170 keys = self.shell.GetKeys(person['key_ids'])
172 # Delete all stale keys
175 if new_key != key['key']:
176 self.shell.DeleteKey(key['key_id'])
180 self.shell.AddPersonKey(pointer, {'key_type': 'ssh', 'key': new_key})
183 self.shell.UpdateNode(pointer, new_sfa_record)
189 def remove (self, sfa_record):
190 type=sfa_record['type']
191 pointer=sfa_record['pointer']
193 persons = self.shell.GetPersons(pointer)
194 # only delete this person if he has site ids. if he doesnt, it probably means
195 # he was just removed from a site, not actually deleted
196 if persons and persons[0]['site_ids']:
197 self.shell.DeletePerson(pointer)
198 elif type == 'slice':
199 if self.shell.GetSlices(pointer):
200 self.shell.DeleteSlice(pointer)
202 if self.shell.GetNodes(pointer):
203 self.shell.DeleteNode(pointer)
204 elif type == 'authority':
205 if self.shell.GetSites(pointer):
206 self.shell.DeleteSite(pointer)
215 # Convert SFA fields to PLC fields for use when registering or updating
216 # registry record in the PLC database
219 def sfa_fields_to_pl_fields(self, type, hrn, sfa_record):
224 pl_record["name"] = hrn_to_pl_slicename(hrn)
225 if "instantiation" in sfa_record:
226 pl_record['instantiation']=sfa_record['instantiation']
228 pl_record["instantiation"] = "plc-instantiated"
229 if "url" in sfa_record:
230 pl_record["url"] = sfa_record["url"]
231 if "description" in sfa_record:
232 pl_record["description"] = sfa_record["description"]
233 if "expires" in sfa_record:
234 date = utcparse(sfa_record['expires'])
235 expires = datetime_to_epoch(date)
236 pl_record["expires"] = expires
239 if not "hostname" in pl_record:
240 # fetch from sfa_record
241 if "hostname" not in sfa_record:
242 raise MissingSfaInfo("hostname")
243 pl_record["hostname"] = sfa_record["hostname"]
244 if "model" in sfa_record:
245 pl_record["model"] = sfa_record["model"]
247 pl_record["model"] = "geni"
249 elif type == "authority":
250 pl_record["login_base"] = PlXrn(xrn=hrn,type='authority').pl_login_base()
251 if "name" not in sfa_record:
252 pl_record["name"] = hrn
253 if "abbreviated_name" not in sfa_record:
254 pl_record["abbreviated_name"] = hrn
255 if "enabled" not in sfa_record:
256 pl_record["enabled"] = True
257 if "is_public" not in sfa_record:
258 pl_record["is_public"] = True
263 def fill_record_info(self, records):
265 Given a (list of) SFA record, fill in the PLC specific
266 and SFA specific fields in the record.
268 if not isinstance(records, list):
271 self.fill_record_pl_info(records)
272 self.fill_record_hrns(records)
273 self.fill_record_sfa_info(records)
276 def fill_record_pl_info(self, records):
278 Fill in the planetlab specific fields of a SFA record. This
279 involves calling the appropriate PLC method to retrieve the
280 database record for the object.
282 @param record: record to fill in field (in/out param)
285 node_ids, site_ids, slice_ids = [], [], []
286 person_ids, key_ids = [], []
287 type_map = {'node': node_ids, 'authority': site_ids,
288 'slice': slice_ids, 'user': person_ids}
290 for record in records:
291 for type in type_map:
292 if type == record['type']:
293 type_map[type].append(record['pointer'])
296 nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
298 node_list = self.shell.GetNodes(node_ids)
299 nodes = list_to_dict(node_list, 'node_id')
301 site_list = self.shell.GetSites(site_ids)
302 sites = list_to_dict(site_list, 'site_id')
304 slice_list = self.shell.GetSlices(slice_ids)
305 slices = list_to_dict(slice_list, 'slice_id')
307 person_list = self.shell.GetPersons(person_ids)
308 persons = list_to_dict(person_list, 'person_id')
309 for person in persons:
310 key_ids.extend(persons[person]['key_ids'])
312 pl_records = {'node': nodes, 'authority': sites,
313 'slice': slices, 'user': persons}
316 key_list = self.shell.GetKeys(key_ids)
317 keys = list_to_dict(key_list, 'key_id')
320 for record in records:
321 # records with pointer==-1 do not have plc info.
322 # for example, the top level authority records which are
323 # authorities, but not PL "sites"
324 if record['pointer'] == -1:
327 for type in pl_records:
328 if record['type'] == type:
329 if record['pointer'] in pl_records[type]:
330 record.update(pl_records[type][record['pointer']])
333 if record['type'] == 'user':
334 if 'key_ids' not in record:
335 logger.info("user record has no 'key_ids' - need to import from myplc ?")
337 pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys]
338 record['keys'] = pubkeys
342 def fill_record_hrns(self, records):
344 convert pl ids to hrns
348 slice_ids, person_ids, site_ids, node_ids = [], [], [], []
349 for record in records:
350 if 'site_id' in record:
351 site_ids.append(record['site_id'])
352 if 'site_ids' in record:
353 site_ids.extend(record['site_ids'])
354 if 'person_ids' in record:
355 person_ids.extend(record['person_ids'])
356 if 'slice_ids' in record:
357 slice_ids.extend(record['slice_ids'])
358 if 'node_ids' in record:
359 node_ids.extend(record['node_ids'])
362 slices, persons, sites, nodes = {}, {}, {}, {}
364 site_list = self.shell.GetSites(site_ids, ['site_id', 'login_base'])
365 sites = list_to_dict(site_list, 'site_id')
367 person_list = self.shell.GetPersons(person_ids, ['person_id', 'email'])
368 persons = list_to_dict(person_list, 'person_id')
370 slice_list = self.shell.GetSlices(slice_ids, ['slice_id', 'name'])
371 slices = list_to_dict(slice_list, 'slice_id')
373 node_list = self.shell.GetNodes(node_ids, ['node_id', 'hostname'])
374 nodes = list_to_dict(node_list, 'node_id')
376 # convert ids to hrns
377 for record in records:
378 # get all relevant data
379 type = record['type']
380 pointer = record['pointer']
386 if 'site_id' in record:
387 site = sites[record['site_id']]
388 login_base = site['login_base']
389 record['site'] = ".".join([auth_hrn, login_base])
390 if 'person_ids' in record:
391 emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
392 if person_id in persons]
393 usernames = [email.split('@')[0] for email in emails]
394 person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
395 record['persons'] = person_hrns
396 if 'slice_ids' in record:
397 slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
398 if slice_id in slices]
399 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
400 record['slices'] = slice_hrns
401 if 'node_ids' in record:
402 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
404 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
405 record['nodes'] = node_hrns
406 if 'site_ids' in record:
407 login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
409 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
410 record['sites'] = site_hrns
412 if 'expires' in record:
413 date = utcparse(record['expires'])
414 datestring = datetime_to_string(date)
415 record['expires'] = datestring
419 def fill_record_sfa_info(self, records):
421 def startswith(prefix, values):
422 return [value for value in values if value.startswith(prefix)]
427 for record in records:
428 person_ids.extend(record.get("person_ids", []))
429 site_ids.extend(record.get("site_ids", []))
430 if 'site_id' in record:
431 site_ids.append(record['site_id'])
433 # get all pis from the sites we've encountered
434 # and store them in a dictionary keyed on site_id
437 pi_filter = {'|roles': ['pi'], '|site_ids': site_ids}
438 pi_list = self.shell.GetPersons(pi_filter, ['person_id', 'site_ids'])
440 # we will need the pi's hrns also
441 person_ids.append(pi['person_id'])
443 # we also need to keep track of the sites these pis
445 for site_id in pi['site_ids']:
446 if site_id in site_pis:
447 site_pis[site_id].append(pi)
449 site_pis[site_id] = [pi]
451 # get sfa records for all records associated with these records.
452 # we'll replace pl ids (person_ids) with hrns from the sfa records
455 # get the registry records
456 person_list, persons = [], {}
457 person_list = dbsession.query (RegRecord).filter(RegRecord.pointer.in_(person_ids))
458 # create a hrns keyed on the sfa record's pointer.
459 # Its possible for multiple records to have the same pointer so
460 # the dict's value will be a list of hrns.
461 persons = defaultdict(list)
462 for person in person_list:
463 persons[person.pointer].append(person)
466 pl_person_list, pl_persons = [], {}
467 pl_person_list = self.shell.GetPersons(person_ids, ['person_id', 'roles'])
468 pl_persons = list_to_dict(pl_person_list, 'person_id')
471 for record in records:
472 # skip records with no pl info (top level authorities)
473 #if record['pointer'] == -1:
476 type = record['type']
477 logger.info("fill_record_sfa_info - incoming record typed %s"%type)
478 if (type == "slice"):
479 # all slice users are researchers
480 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
482 record['researcher'] = []
483 for person_id in record.get('person_ids', []):
484 hrns = [person.hrn for person in persons[person_id]]
485 record['researcher'].extend(hrns)
487 # pis at the slice's site
488 if 'site_id' in record and record['site_id'] in site_pis:
489 pl_pis = site_pis[record['site_id']]
490 pi_ids = [pi['person_id'] for pi in pl_pis]
491 for person_id in pi_ids:
492 hrns = [person.hrn for person in persons[person_id]]
493 record['PI'].extend(hrns)
494 record['geni_creator'] = record['PI']
496 elif (type.startswith("authority")):
498 logger.info("fill_record_sfa_info - authority xherex")
499 if record['pointer'] != -1:
501 record['operator'] = []
503 for pointer in record.get('person_ids', []):
504 if pointer not in persons or pointer not in pl_persons:
505 # this means there is not sfa or pl record for this user
507 hrns = [person.hrn for person in persons[pointer]]
508 roles = pl_persons[pointer]['roles']
510 record['PI'].extend(hrns)
512 record['operator'].extend(hrns)
514 record['owner'].extend(hrns)
515 # xxx TODO: OrganizationName
516 elif (type == "node"):
517 sfa_info['dns'] = record.get("hostname", "")
518 # xxx TODO: URI, LatLong, IP, DNS
520 elif (type == "user"):
521 logger.info('setting user.email')
522 sfa_info['email'] = record.get("email", "")
523 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
524 sfa_info['geni_certificate'] = record['gid']
525 # xxx TODO: PostalAddress, Phone
526 record.update(sfa_info)
530 # plcapi works by changes, compute what needs to be added/deleted
531 def update_relation (self, subject_type, target_type, relation_name, subject_id, target_ids):
532 # hard-wire the code for slice/user for now, could be smarter if needed
533 if subject_type =='slice' and target_type == 'user' and relation_name == 'researcher':
534 subject=self.shell.GetSlices (subject_id)[0]
535 current_target_ids = subject['person_ids']
536 add_target_ids = list ( set (target_ids).difference(current_target_ids))
537 del_target_ids = list ( set (current_target_ids).difference(target_ids))
538 logger.debug ("subject_id = %s (type=%s)"%(subject_id,type(subject_id)))
539 for target_id in add_target_ids:
540 self.shell.AddPersonToSlice (target_id,subject_id)
541 logger.debug ("add_target_id = %s (type=%s)"%(target_id,type(target_id)))
542 for target_id in del_target_ids:
543 logger.debug ("del_target_id = %s (type=%s)"%(target_id,type(target_id)))
544 self.shell.DeletePersonFromSlice (target_id, subject_id)
545 elif subject_type == 'authority' and target_type == 'user' and relation_name == 'pi':
546 # due to the plcapi limitations this means essentially adding pi role to all people in the list
547 # it's tricky to remove any pi role here, although it might be desirable
548 persons = self.shell.GetPersons (target_ids)
549 for person in persons:
550 if 'pi' not in person['roles']:
551 self.shell.AddRoleToPerson('pi',person['person_id'])
553 logger.info('unexpected relation %s to maintain, %s -> %s'%(relation_name,subject_type,target_type))
556 ########################################
557 ########## aggregate oriented
558 ########################################
560 def testbed_name (self): return "myplc"
562 def aggregate_version (self):
565 # first 2 args are None in case of resource discovery
566 def list_resources (self, version=None, options={}):
567 aggregate = PlAggregate(self)
568 rspec = aggregate.list_resources(version=version, options=options)
571 def describe(self, urns, version, options={}):
572 aggregate = PlAggregate(self)
573 return aggregate.describe(urns, version=version, options=options)
575 def status (self, urns, options={}):
576 aggregate = PlAggregate(self)
577 desc = aggregate.describe(urns)
578 return desc['geni_slivers']
580 # find out where this slice is currently running
581 slicename = hrn_to_pl_slicename(slice_hrn)
583 slices = self.shell.GetSlices([slicename], ['slice_id', 'node_ids','person_ids','name','expires'])
585 raise SliverDoesNotExist("%s (used %s as slicename internally)" % (slice_hrn, slicename))
588 # report about the local nodes only
589 nodes = self.shell.GetNodes({'node_id':slice['node_ids'],'peer_id':None},
590 ['node_id', 'hostname', 'site_id', 'boot_state', 'last_contact'])
593 raise SliverDoesNotExist("You have not allocated any slivers here")
597 if slice['person_ids']:
598 persons = self.shell.GetPersons(slice['person_ids'], ['key_ids'])
599 key_ids = [key_id for person in persons for key_id in person['key_ids']]
600 person_keys = self.shell.GetKeys(key_ids)
601 keys = [key['key'] for key in person_keys]
603 user.update({'urn': slice_urn,
604 'login': slice['name'],
609 site_ids = [node['site_id'] for node in nodes]
612 result['geni_urn'] = slice_urn
613 result['pl_login'] = slice['name']
614 result['pl_expires'] = datetime_to_string(utcparse(slice['expires']))
615 result['geni_expires'] = datetime_to_string(utcparse(slice['expires']))
620 res['pl_hostname'] = node['hostname']
621 res['pl_boot_state'] = node['boot_state']
622 res['pl_last_contact'] = node['last_contact']
623 res['geni_expires'] = datetime_to_string(utcparse(slice['expires']))
624 if node['last_contact'] is not None:
626 res['pl_last_contact'] = datetime_to_string(utcparse(node['last_contact']))
627 sliver_id = "%s:%s" % (slice['slice_id'], node['node_id'])
628 sliver_xrn = Xrn(slice_urn, id = sliver_id)
629 sliver_xrn.set_authority(self.hrn)
630 res['geni_urn'] = sliver_xrn.get_urn()
631 if node['boot_state'] == 'boot':
632 res['geni_status'] = 'ready'
634 res['geni_status'] = 'failed'
635 res['geni_allocation_status'] = 'geni_provisioned'
637 res['geni_error'] = ''
638 res['users'] = [user]
639 resources.append(res)
641 result['geni_resources'] = resources
644 def allocate (self, urn, rspec_string, options={}):
646 aggregate = PlAggregate(self)
647 slices = PlSlices(self)
648 peer = slices.get_peer(xrn.get_hrn())
649 sfa_peer = slices.get_sfa_peer(xrn.get_hrn())
651 users = options.get('geni_users', [])
653 slice_record = users[0].get('slice_record', {})
656 rspec = RSpec(rspec_string)
657 requested_attributes = rspec.version.get_slice_attributes()
659 # ensure site record exists
660 site = slices.verify_site(xrn.hrn, slice_record, peer, sfa_peer, options=options)
661 # ensure slice record exists
662 slice = slices.verify_slice(xrn.hrn, slice_record, peer, sfa_peer, options=options)
663 # ensure person records exists
664 persons = slices.verify_persons(xrn.hrn, slice, users, peer, sfa_peer, options=options)
665 # ensure slice attributes exists
666 slices.verify_slice_attributes(slice, requested_attributes, options=options)
668 # add/remove slice from nodes
669 requested_slivers = []
670 for node in rspec.version.get_nodes_with_slivers():
672 if node.get('component_name'):
673 hostname = node.get('component_name').strip()
674 elif node.get('component_id'):
675 hostname = xrn_to_hostname(node.get('component_id').strip())
677 requested_slivers.append(hostname)
678 nodes = slices.verify_slice_nodes(slice, requested_slivers, peer)
680 # add/remove links links
681 slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
684 requested_leases = []
686 for lease in rspec.version.get_leases():
688 if not lease.get('lease_id'):
689 requested_lease['hostname'] = xrn_to_hostname(lease.get('component_id').strip())
690 requested_lease['start_time'] = lease.get('start_time')
691 requested_lease['duration'] = lease.get('duration')
693 kept_leases.append(int(lease['lease_id']))
694 if requested_lease.get('hostname'):
695 requested_leases.append(requested_lease)
697 leases = slices.verify_slice_leases(slice, requested_leases, kept_leases, peer)
699 # handle MyPLC peer association.
700 # only used by plc and ple.
701 slices.handle_peer(site, slice, persons, peer)
703 return aggregate.describe(slice_xrn=xrn.get_urn(), version=rspec.version)
705 def provision(self, urns, options={}):
706 return self.describe(urns, options=options)
708 def delete(self, urns, options={}):
712 xrn = PlXrn(xrn=urn, type='slice')
713 names.append(xrn.pl_slicename())
716 slices = self.shell.GetSlices({'name': names})
718 raise SearchFailed(urns)
723 node_ids = slice['node_ids']
725 # determine if this is a peer slice
726 # xxx I wonder if this would not need to use PlSlices.get_peer instead
727 # in which case plc.peers could be deprecated as this here
728 # is the only/last call to this last method in plc.peers
729 peer = peers.get_peer(self, slice_hrn)
732 self.shell.UnBindObjectFromPeer('slice', slice['slice_id'], peer)
733 self.shell.DeleteSliceFromNodes(slice['slice_id'], node_ids)
736 self.shell.BindObjectToPeer('slice', slice['slice_id'], peer, slice['peer_slice_id'])
739 def renew (self, urns, expiration_time, options={}):
740 # we can only renew slices, not individual slivers. ignore sliver
744 xrn = PlXrn(urn=urn, type='slice')
745 names.append(xrn.pl_slicename())
746 slices = self.shell.GetSlices(names, ['slice_id'])
748 raise SearchFailed(urns)
750 requested_time = utcparse(expiration_time)
751 record = {'expires': int(datetime_to_epoch(requested_time))}
753 self.shell.UpdateSlice(slice['slice_id'], record)
758 def perform_operational_action (self, urns, action, options={}):
759 # MyPLC doesn't support operational actions. Lets pretend like it
760 # supports start, but reject everything else.
761 action = action.lower()
762 if action == 'geni_start':
765 raise UnsupportedOperation(action)
766 description = self.describe(urns)
767 return description['geni_slivers']
769 # set the 'enabled' tag to 0
770 def shutdown (self, xrn, options={}):
771 xrn = PlXrn(xrn=xrn, type='slice')
772 slicename = xrn.pl_slicename()
773 slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
775 raise RecordNotFound(slice_hrn)
776 slice_id = slices[0]['slice_id']
777 slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'})
779 self.shell.AddSliceTag(slice_id, 'enabled', '0')
780 elif slice_tags[0]['value'] != "0":
781 tag_id = slice_tags[0]['slice_tag_id']
782 self.shell.UpdateSliceTag(tag_id, '0')