3 from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \
4 RecordNotFound, SfaNotImplemented, SliverDoesNotExist, SearchFailed, \
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.alchemy import dbsession
14 from sfa.storage.model import RegRecord, SliverAllocation
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 import sfa.planetlab.peers as peers
25 from sfa.planetlab.plaggregate import PlAggregate
26 from sfa.planetlab.plslices import PlSlices
27 from sfa.planetlab.plxrn import PlXrn, slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, xrn_to_hostname
30 def list_to_dict(recs, key):
32 convert a list of dictionaries into a dictionary keyed on the
33 specified dictionary key
35 return dict ( [ (rec[key],rec) for rec in recs ] )
38 # PlShell is just an xmlrpc serverproxy where methods
39 # can be sent as-is; it takes care of authentication
40 # from the global config
42 class PlDriver (Driver):
44 # the cache instance is a class member so it survives across incoming requests
47 def __init__ (self, config):
48 Driver.__init__ (self, config)
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 ########################################
57 ########## registry oriented
58 ########################################
60 def augment_records_with_testbed_info (self, sfa_records):
61 return self.fill_record_info (sfa_records)
64 def register (self, sfa_record, hrn, pub_key):
65 type = sfa_record['type']
66 pl_record = self.sfa_fields_to_pl_fields(type, hrn, sfa_record)
68 if type == 'authority':
69 sites = self.shell.GetSites([pl_record['login_base']])
71 # xxx when a site gets registered through SFA we need to set its max_slices
72 if 'max_slices' not in pl_record:
73 pl_record['max_slices']=2
74 pointer = self.shell.AddSite(pl_record)
76 pointer = sites[0]['site_id']
79 acceptable_fields=['url', 'instantiation', 'name', 'description']
80 for key in pl_record.keys():
81 if key not in acceptable_fields:
83 slices = self.shell.GetSlices([pl_record['name']])
85 pointer = self.shell.AddSlice(pl_record)
87 pointer = slices[0]['slice_id']
90 persons = self.shell.GetPersons({'email':sfa_record['email']})
92 for key in ['first_name','last_name']:
93 if key not in sfa_record: sfa_record[key]='*from*sfa*'
94 pointer = self.shell.AddPerson(dict(sfa_record))
96 pointer = persons[0]['person_id']
98 if 'enabled' in sfa_record and sfa_record['enabled']:
99 self.shell.UpdatePerson(pointer, {'enabled': sfa_record['enabled']})
100 # add this person to the site only if she is being added for the first
101 # time by sfa and doesont already exist in plc
102 if not persons or not persons[0]['site_ids']:
103 login_base = get_leaf(sfa_record['authority'])
104 self.shell.AddPersonToSite(pointer, login_base)
106 # What roles should this user have?
108 if 'roles' in sfa_record:
109 # if specified in xml, but only low-level roles
110 roles = [ role for role in sfa_record['roles'] if role in ['user','tech'] ]
111 # at least user if no other cluse could be found
115 self.shell.AddRoleToPerson(role, pointer)
118 self.shell.AddPersonKey(pointer, {'key_type' : 'ssh', 'key' : pub_key})
121 login_base = PlXrn(xrn=sfa_record['authority'],type='node').pl_login_base()
122 nodes = self.shell.GetNodes([pl_record['hostname']])
124 pointer = self.shell.AddNode(login_base, pl_record)
126 pointer = nodes[0]['node_id']
131 # xxx actually old_sfa_record comes filled with plc stuff as well in the original code
132 def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
133 pointer = old_sfa_record['pointer']
134 type = old_sfa_record['type']
136 # new_key implemented for users only
137 if new_key and type not in [ 'user' ]:
138 raise UnknownSfaType(type)
140 if (type == "authority"):
141 self.shell.UpdateSite(pointer, new_sfa_record)
143 elif type == "slice":
144 pl_record=self.sfa_fields_to_pl_fields(type, hrn, new_sfa_record)
145 if 'name' in pl_record:
146 pl_record.pop('name')
147 self.shell.UpdateSlice(pointer, pl_record)
150 # SMBAKER: UpdatePerson only allows a limited set of fields to be
151 # updated. Ideally we should have a more generic way of doing
152 # this. I copied the field names from UpdatePerson.py...
154 all_fields = new_sfa_record
155 for key in all_fields.keys():
156 if key in ['first_name', 'last_name', 'title', 'email',
157 'password', 'phone', 'url', 'bio', 'accepted_aup',
159 update_fields[key] = all_fields[key]
160 # when updating a user, we always get a 'email' field at this point
161 # this is because 'email' is a native field in the RegUser object...
162 if 'email' in update_fields and not update_fields['email']:
163 del update_fields['email']
164 self.shell.UpdatePerson(pointer, update_fields)
167 # must check this key against the previous one if it exists
168 persons = self.shell.GetPersons([pointer], ['key_ids'])
170 keys = person['key_ids']
171 keys = self.shell.GetKeys(person['key_ids'])
173 # Delete all stale keys
176 if new_key != key['key']:
177 self.shell.DeleteKey(key['key_id'])
181 self.shell.AddPersonKey(pointer, {'key_type': 'ssh', 'key': new_key})
184 self.shell.UpdateNode(pointer, new_sfa_record)
190 def remove (self, sfa_record):
191 type=sfa_record['type']
192 pointer=sfa_record['pointer']
194 persons = self.shell.GetPersons(pointer)
195 # only delete this person if he has site ids. if he doesnt, it probably means
196 # he was just removed from a site, not actually deleted
197 if persons and persons[0]['site_ids']:
198 self.shell.DeletePerson(pointer)
199 elif type == 'slice':
200 if self.shell.GetSlices(pointer):
201 self.shell.DeleteSlice(pointer)
203 if self.shell.GetNodes(pointer):
204 self.shell.DeleteNode(pointer)
205 elif type == 'authority':
206 if self.shell.GetSites(pointer):
207 self.shell.DeleteSite(pointer)
216 # Convert SFA fields to PLC fields for use when registering or updating
217 # registry record in the PLC database
220 def sfa_fields_to_pl_fields(self, type, hrn, sfa_record):
225 pl_record["name"] = hrn_to_pl_slicename(hrn)
226 if "instantiation" in sfa_record:
227 pl_record['instantiation']=sfa_record['instantiation']
229 pl_record["instantiation"] = "plc-instantiated"
230 if "url" in sfa_record:
231 pl_record["url"] = sfa_record["url"]
232 if "description" in sfa_record:
233 pl_record["description"] = sfa_record["description"]
234 if "expires" in sfa_record:
235 date = utcparse(sfa_record['expires'])
236 expires = datetime_to_epoch(date)
237 pl_record["expires"] = expires
240 if not "hostname" in pl_record:
241 # fetch from sfa_record
242 if "hostname" not in sfa_record:
243 raise MissingSfaInfo("hostname")
244 pl_record["hostname"] = sfa_record["hostname"]
245 if "model" in sfa_record:
246 pl_record["model"] = sfa_record["model"]
248 pl_record["model"] = "geni"
250 elif type == "authority":
251 pl_record["login_base"] = PlXrn(xrn=hrn,type='authority').pl_login_base()
252 if "name" not in sfa_record:
253 pl_record["name"] = hrn
254 if "abbreviated_name" not in sfa_record:
255 pl_record["abbreviated_name"] = hrn
256 if "enabled" not in sfa_record:
257 pl_record["enabled"] = True
258 if "is_public" not in sfa_record:
259 pl_record["is_public"] = True
264 def fill_record_info(self, records):
266 Given a (list of) SFA record, fill in the PLC specific
267 and SFA specific fields in the record.
269 if not isinstance(records, list):
272 self.fill_record_pl_info(records)
273 self.fill_record_hrns(records)
274 self.fill_record_sfa_info(records)
277 def fill_record_pl_info(self, records):
279 Fill in the planetlab specific fields of a SFA record. This
280 involves calling the appropriate PLC method to retrieve the
281 database record for the object.
283 @param record: record to fill in field (in/out param)
286 node_ids, site_ids, slice_ids = [], [], []
287 person_ids, key_ids = [], []
288 type_map = {'node': node_ids, 'authority': site_ids,
289 'slice': slice_ids, 'user': person_ids}
291 for record in records:
292 for type in type_map:
293 if type == record['type']:
294 type_map[type].append(record['pointer'])
297 nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
299 node_list = self.shell.GetNodes(node_ids)
300 nodes = list_to_dict(node_list, 'node_id')
302 site_list = self.shell.GetSites(site_ids)
303 sites = list_to_dict(site_list, 'site_id')
305 slice_list = self.shell.GetSlices(slice_ids)
306 slices = list_to_dict(slice_list, 'slice_id')
308 person_list = self.shell.GetPersons(person_ids)
309 persons = list_to_dict(person_list, 'person_id')
310 for person in persons:
311 key_ids.extend(persons[person]['key_ids'])
313 pl_records = {'node': nodes, 'authority': sites,
314 'slice': slices, 'user': persons}
317 key_list = self.shell.GetKeys(key_ids)
318 keys = list_to_dict(key_list, 'key_id')
321 for record in records:
322 # records with pointer==-1 do not have plc info.
323 # for example, the top level authority records which are
324 # authorities, but not PL "sites"
325 if record['pointer'] == -1:
328 for type in pl_records:
329 if record['type'] == type:
330 if record['pointer'] in pl_records[type]:
331 record.update(pl_records[type][record['pointer']])
334 if record['type'] == 'user':
335 if 'key_ids' not in record:
336 logger.info("user record has no 'key_ids' - need to import from myplc ?")
338 pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys]
339 record['keys'] = pubkeys
343 def fill_record_hrns(self, records):
345 convert pl ids to hrns
349 slice_ids, person_ids, site_ids, node_ids = [], [], [], []
350 for record in records:
351 if 'site_id' in record:
352 site_ids.append(record['site_id'])
353 if 'site_ids' in record:
354 site_ids.extend(record['site_ids'])
355 if 'person_ids' in record:
356 person_ids.extend(record['person_ids'])
357 if 'slice_ids' in record:
358 slice_ids.extend(record['slice_ids'])
359 if 'node_ids' in record:
360 node_ids.extend(record['node_ids'])
363 slices, persons, sites, nodes = {}, {}, {}, {}
365 site_list = self.shell.GetSites(site_ids, ['site_id', 'login_base'])
366 sites = list_to_dict(site_list, 'site_id')
368 person_list = self.shell.GetPersons(person_ids, ['person_id', 'email'])
369 persons = list_to_dict(person_list, 'person_id')
371 slice_list = self.shell.GetSlices(slice_ids, ['slice_id', 'name'])
372 slices = list_to_dict(slice_list, 'slice_id')
374 node_list = self.shell.GetNodes(node_ids, ['node_id', 'hostname'])
375 nodes = list_to_dict(node_list, 'node_id')
377 # convert ids to hrns
378 for record in records:
379 # get all relevant data
380 type = record['type']
381 pointer = record['pointer']
387 if 'site_id' in record:
388 site = sites[record['site_id']]
389 login_base = site['login_base']
390 record['site'] = ".".join([auth_hrn, login_base])
391 if 'person_ids' in record:
392 emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
393 if person_id in persons]
394 usernames = [email.split('@')[0] for email in emails]
395 person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
396 record['persons'] = person_hrns
397 if 'slice_ids' in record:
398 slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
399 if slice_id in slices]
400 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
401 record['slices'] = slice_hrns
402 if 'node_ids' in record:
403 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
405 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
406 record['nodes'] = node_hrns
407 if 'site_ids' in record:
408 login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
410 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
411 record['sites'] = site_hrns
413 if 'expires' in record:
414 date = utcparse(record['expires'])
415 datestring = datetime_to_string(date)
416 record['expires'] = datestring
420 def fill_record_sfa_info(self, records):
422 def startswith(prefix, values):
423 return [value for value in values if value.startswith(prefix)]
428 for record in records:
429 person_ids.extend(record.get("person_ids", []))
430 site_ids.extend(record.get("site_ids", []))
431 if 'site_id' in record:
432 site_ids.append(record['site_id'])
434 # get all pis from the sites we've encountered
435 # and store them in a dictionary keyed on site_id
438 pi_filter = {'|roles': ['pi'], '|site_ids': site_ids}
439 pi_list = self.shell.GetPersons(pi_filter, ['person_id', 'site_ids'])
441 # we will need the pi's hrns also
442 person_ids.append(pi['person_id'])
444 # we also need to keep track of the sites these pis
446 for site_id in pi['site_ids']:
447 if site_id in site_pis:
448 site_pis[site_id].append(pi)
450 site_pis[site_id] = [pi]
452 # get sfa records for all records associated with these records.
453 # we'll replace pl ids (person_ids) with hrns from the sfa records
456 # get the registry records
457 person_list, persons = [], {}
458 person_list = dbsession.query (RegRecord).filter(RegRecord.pointer.in_(person_ids))
459 # create a hrns keyed on the sfa record's pointer.
460 # Its possible for multiple records to have the same pointer so
461 # the dict's value will be a list of hrns.
462 persons = defaultdict(list)
463 for person in person_list:
464 persons[person.pointer].append(person)
467 pl_person_list, pl_persons = [], {}
468 pl_person_list = self.shell.GetPersons(person_ids, ['person_id', 'roles'])
469 pl_persons = list_to_dict(pl_person_list, 'person_id')
472 for record in records:
473 # skip records with no pl info (top level authorities)
474 #if record['pointer'] == -1:
477 type = record['type']
478 logger.info("fill_record_sfa_info - incoming record typed %s"%type)
479 if (type == "slice"):
480 # all slice users are researchers
481 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
483 record['researcher'] = []
484 for person_id in record.get('person_ids', []):
485 hrns = [person.hrn for person in persons[person_id]]
486 record['researcher'].extend(hrns)
488 # pis at the slice's site
489 if 'site_id' in record and record['site_id'] in site_pis:
490 pl_pis = site_pis[record['site_id']]
491 pi_ids = [pi['person_id'] for pi in pl_pis]
492 for person_id in pi_ids:
493 hrns = [person.hrn for person in persons[person_id]]
494 record['PI'].extend(hrns)
495 record['geni_creator'] = record['PI']
497 elif (type.startswith("authority")):
499 logger.info("fill_record_sfa_info - authority xherex")
500 if record['pointer'] != -1:
502 record['operator'] = []
504 for pointer in record.get('person_ids', []):
505 if pointer not in persons or pointer not in pl_persons:
506 # this means there is not sfa or pl record for this user
508 hrns = [person.hrn for person in persons[pointer]]
509 roles = pl_persons[pointer]['roles']
511 record['PI'].extend(hrns)
513 record['operator'].extend(hrns)
515 record['owner'].extend(hrns)
516 # xxx TODO: OrganizationName
517 elif (type == "node"):
518 sfa_info['dns'] = record.get("hostname", "")
519 # xxx TODO: URI, LatLong, IP, DNS
521 elif (type == "user"):
522 logger.info('setting user.email')
523 sfa_info['email'] = record.get("email", "")
524 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
525 sfa_info['geni_certificate'] = record['gid']
526 # xxx TODO: PostalAddress, Phone
527 record.update(sfa_info)
531 # plcapi works by changes, compute what needs to be added/deleted
532 def update_relation (self, subject_type, target_type, relation_name, subject_id, target_ids):
533 # hard-wire the code for slice/user for now, could be smarter if needed
534 if subject_type =='slice' and target_type == 'user' and relation_name == 'researcher':
535 subject=self.shell.GetSlices (subject_id)[0]
536 current_target_ids = subject['person_ids']
537 add_target_ids = list ( set (target_ids).difference(current_target_ids))
538 del_target_ids = list ( set (current_target_ids).difference(target_ids))
539 logger.debug ("subject_id = %s (type=%s)"%(subject_id,type(subject_id)))
540 for target_id in add_target_ids:
541 self.shell.AddPersonToSlice (target_id,subject_id)
542 logger.debug ("add_target_id = %s (type=%s)"%(target_id,type(target_id)))
543 for target_id in del_target_ids:
544 logger.debug ("del_target_id = %s (type=%s)"%(target_id,type(target_id)))
545 self.shell.DeletePersonFromSlice (target_id, subject_id)
546 elif subject_type == 'authority' and target_type == 'user' and relation_name == 'pi':
547 # due to the plcapi limitations this means essentially adding pi role to all people in the list
548 # it's tricky to remove any pi role here, although it might be desirable
549 persons = self.shell.GetPersons (target_ids)
550 for person in persons:
551 if 'pi' not in person['roles']:
552 self.shell.AddRoleToPerson('pi',person['person_id'])
554 logger.info('unexpected relation %s to maintain, %s -> %s'%(relation_name,subject_type,target_type))
557 ########################################
558 ########## aggregate oriented
559 ########################################
561 def testbed_name (self): return "myplc"
563 def aggregate_version (self):
566 # first 2 args are None in case of resource discovery
567 def list_resources (self, version=None, options={}):
568 aggregate = PlAggregate(self)
569 rspec = aggregate.list_resources(version=version, options=options)
572 def describe(self, urns, version, options={}, allocation_status=None):
573 aggregate = PlAggregate(self)
574 return aggregate.describe(urns, version=version, options=options)
576 def status (self, urns, options={}):
577 aggregate = PlAggregate(self)
578 desc = aggregate.describe(urns)
579 status = {'geni_urn': desc['geni_urn'],
580 'geni_slivers': desc['geni_slivers']}
583 def allocate (self, urn, rspec_string, options={}):
585 aggregate = PlAggregate(self)
586 slices = PlSlices(self)
587 peer = slices.get_peer(xrn.get_hrn())
588 sfa_peer = slices.get_sfa_peer(xrn.get_hrn())
590 users = options.get('geni_users', [])
592 slice_record = users[0].get('slice_record', {})
595 rspec = RSpec(rspec_string)
596 requested_attributes = rspec.version.get_slice_attributes()
598 # ensure site record exists
599 site = slices.verify_site(xrn.hrn, slice_record, peer, sfa_peer, options=options)
600 # ensure slice record exists
601 slice = slices.verify_slice(xrn.hrn, slice_record, peer, sfa_peer, options=options)
602 # ensure person records exists
603 persons = slices.verify_persons(xrn.hrn, slice, users, peer, sfa_peer, options=options)
604 # ensure slice attributes exists
605 slices.verify_slice_attributes(slice, requested_attributes, options=options)
607 # add/remove slice from nodes
608 requested_slivers = []
609 for node in rspec.version.get_nodes_with_slivers():
611 if node.get('component_name'):
612 hostname = node.get('component_name').strip()
613 elif node.get('component_id'):
614 hostname = xrn_to_hostname(node.get('component_id').strip())
616 requested_slivers.append(hostname)
617 nodes = slices.verify_slice_nodes(slice, requested_slivers, peer)
619 # update all sliver allocation states setting then to geni_allocated
622 sliver_hrn = '%s.%s-%s' % (self.hrn, slice['slice_id'], node['node_id'])
623 sliver_id = Xrn(sliver_hrn, type='sliver').urn
624 sliver_ids.append(sliver_id)
625 SliverAllocation.set_allocations(sliver_ids, 'geni_allocated')
627 # add/remove links links
628 slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
631 requested_leases = []
633 for lease in rspec.version.get_leases():
635 if not lease.get('lease_id'):
636 requested_lease['hostname'] = xrn_to_hostname(lease.get('component_id').strip())
637 requested_lease['start_time'] = lease.get('start_time')
638 requested_lease['duration'] = lease.get('duration')
640 kept_leases.append(int(lease['lease_id']))
641 if requested_lease.get('hostname'):
642 requested_leases.append(requested_lease)
644 leases = slices.verify_slice_leases(slice, requested_leases, kept_leases, peer)
645 # handle MyPLC peer association.
646 # only used by plc and ple.
647 slices.handle_peer(site, slice, persons, peer)
649 return aggregate.describe([xrn.get_urn()], version=rspec.version)
651 def provision(self, urns, options={}):
652 # update sliver allocation states and set them to geni_provisioned
653 aggregate = PlAggregate(self)
654 slivers = aggregate.get_slivers(urns)
655 sliver_ids = [sliver['sliver_id'] for sliver in slivers]
656 SliverAllocation.set_allocations(sliver_ids, 'geni_provisioned')
658 return self.describe(urns, None, options=options)
660 def delete(self, urns, options={}):
661 # collect sliver ids so we can update sliver allocation states after
662 # we remove the slivers.
663 aggregate = PlAggregate(self)
664 slivers = aggregate.get_slivers(urns)
665 slice_id = slivers[0]['slice_id']
668 for sliver in slivers:
669 node_ids.append(sliver['node_id'])
670 sliver_ids.append(sliver['sliver_id'])
672 # determine if this is a peer slice
673 # xxx I wonder if this would not need to use PlSlices.get_peer instead
674 # in which case plc.peers could be deprecated as this here
675 # is the only/last call to this last method in plc.peers
676 slice_hrn = PlXrn(auth=self.hrn, slicename=slivers[0]['name']).get_hrn()
677 peer = peers.get_peer(self, slice_hrn)
680 self.shell.UnBindObjectFromPeer('slice', slice_id, peer)
682 self.shell.DeleteSliceFromNodes(slice_id, node_ids)
684 # delete sliver allocation states
685 SliverAllocation.delete_allocations(sliver_ids)
688 self.shell.BindObjectToPeer('slice', slice_id, peer, slice['peer_slice_id'])
690 # prepare return struct
692 for node_id in node_ids:
693 sliver_hrn = '%s.%s-%s' % (self.hrn, slice_id, node_id)
695 {'geni_sliver_urn': Xrn(sliver_hrn, type='sliver').urn,
696 'geni_allocation_status': 'geni_unallocated',
697 'geni_expires': datetime_to_string(utcparse(slivers[0]['expires']))})
700 def renew (self, urns, expiration_time, options={}):
701 # we can only renew slices, not individual slivers. ignore sliver
705 xrn = PlXrn(xrn=urn, type='slice')
706 names.append(xrn.pl_slicename())
707 slices = self.shell.GetSlices(names, ['slice_id'])
709 raise SearchFailed(urns)
711 requested_time = utcparse(expiration_time)
712 record = {'expires': int(datetime_to_epoch(requested_time))}
713 self.shell.UpdateSlice(slice['slice_id'], record)
714 description = self.describe(urns, None, options)
715 return description['geni_slivers']
718 def perform_operational_action (self, urns, action, options={}):
719 # MyPLC doesn't support operational actions. Lets pretend like it
720 # supports start, but reject everything else.
721 action = action.lower()
722 if action not in ['geni_start']:
723 raise UnsupportedOperation(action)
725 # fault if sliver is not full allocated (operational status is geni_pending_allocation)
726 description = self.describe(urns, None, options)
727 for sliver in description['geni_slivers']:
728 if sliver['geni_operational_status'] == 'geni_pending_allocation':
729 raise UnsupportedOperation(action, "Sliver must be fully allocated (operational status is not geni_pending_allocation)")
731 # Perform Operational Action Here
734 geni_slivers = self.describe(urns, None, options)['geni_slivers']
737 # set the 'enabled' tag to 0
738 def shutdown (self, xrn, options={}):
739 xrn = PlXrn(xrn=xrn, type='slice')
740 slicename = xrn.pl_slicename()
741 slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
743 raise RecordNotFound(slice_hrn)
744 slice_id = slices[0]['slice_id']
745 slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'})
747 self.shell.AddSliceTag(slice_id, 'enabled', '0')
748 elif slice_tags[0]['value'] != "0":
749 tag_id = slice_tags[0]['slice_tag_id']
750 self.shell.UpdateSliceTag(tag_id, '0')