From: Mohamed Larabi Date: Tue, 4 Jun 2013 07:52:31 +0000 (+0200) Subject: Refactor Dummy Driver and make it implements AM API v3 natively X-Git-Tag: sfa-3.0-1~18 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;ds=sidebyside;h=bd37ef89eeabf6cead7be45d26129814cbbe76cd;p=sfa.git Refactor Dummy Driver and make it implements AM API v3 natively --- diff --git a/sfa/dummy/README.txt b/sfa/dummy/README.txt index c205e4b4..5436cfed 100644 --- a/sfa/dummy/README.txt +++ b/sfa/dummy/README.txt @@ -19,8 +19,8 @@ STEP-BY-STEP GUIDE : # sfa-config-tty Enter command (u for usual changes, w to save, ? for help) u == sfa_generic_flavour : [dummy] dummy ("dummy" flavour) -== sfa_interface_hrn : [pla] pla (Choose your Authority name) -== sfa_registry_root_auth : [pla] pla (Choose your Authority name) +== sfa_interface_hrn : [pla] topdomain (Choose your Authority name) +== sfa_registry_root_auth : [pla] topdomain (Choose your Authority name) == sfa_registry_host : [localhost] localhost == sfa_aggregate_host : [localhost] localhost == sfa_sm_host : [localhost] localhost @@ -44,22 +44,27 @@ SFA: Aggregate [ OK ] SFA: SliceMgr [ OK ] Enter command (u for usual changes, w to save, ? for help) q -4. Add your user to the dummy testbed and attach it to a slice: - -Edit /usr/lib/python2.7/site-packages/sfa/dummy/dummy_testbed_api_client.py with your user info and run: - -# python /usr/lib/python2.7/site-packages/sfa/dummy/dummy_testbed_api_client.py - 5. Import Dummy testbed data to SFA (users, slices, nodes): # sfaadmin.py reg import_registry +5. Create a user and a slice: + +# sfaadmin.py reg register -t user -x topdomain.dummy.bob -k /root/.ssh/id_rsa.pub -e bob@dummy.net +# sfaadmin.py reg register -t slice -x topdomain.dummy.bob_slice -r topdomain.dummy.bob + 6. Configure you SFI client (http://svn.planet-lab.org/wiki/SFATutorialConfigureSFA#ConfigureSFAClientSFI) +Example of sfi_config: +[sfi] +auth = topdomain.dummy +user = topdomain.dummy.bob +registry = http://localhost:12345/ +sm = http://localhost:12346/ 7. Make a test: update the following command with your already configured Authority name. -# sfi.py list pla.dummy +# sfi.py list topdomain.dummy 8. Now continue testing SFA, have a look at the dummy driver code and write your testbed driver for SFA... Enjoy. diff --git a/sfa/dummy/dummy_testbed_api.py b/sfa/dummy/dummy_testbed_api.py index f37e52c5..2673166d 100644 --- a/sfa/dummy/dummy_testbed_api.py +++ b/sfa/dummy/dummy_testbed_api.py @@ -32,7 +32,7 @@ def FilterList(myfilter, mylist): if 'ids' in key: pass else: - if myfilter[key] != item[key]: + if isinstance(myfilter[key], str) and myfilter[key] != item[key] or isinstance(myfilter[key], list) and item[key] not in myfilter[key]: result.remove(item) break return result diff --git a/sfa/dummy/dummy_testbed_api_client.py b/sfa/dummy/dummy_testbed_api_client.py index e32c15b9..38ba0d3b 100644 --- a/sfa/dummy/dummy_testbed_api_client.py +++ b/sfa/dummy/dummy_testbed_api_client.py @@ -5,11 +5,12 @@ import time dummy_url = "http://localhost:8080" dummy_api = xmlrpclib.ServerProxy(dummy_url) -# Edit the parameters with your user info: +# Add a user: my_user_id = dummy_api.AddUser({'email': 'john.doe@test.net', 'user_name': 'john.doe', 'keys': ['copy here your ssh-rsa public key']}) -# Your user will be attached with the slice named : slice2 : +# Attach the user with the slice named : slice2 : dummy_api.AddUserToSlice({'slice_id': 2, 'user_id': my_user_id}) - -print dummy_api.GetUsers()[-1] -print dummy_api.GetSlices()[-1] +# Display the list of users +print dummy_api.GetUsers() +# Display the list of slices +print dummy_api.GetSlices() diff --git a/sfa/dummy/dummyaggregate.py b/sfa/dummy/dummyaggregate.py index 4630033f..dca47b59 100644 --- a/sfa/dummy/dummyaggregate.py +++ b/sfa/dummy/dummyaggregate.py @@ -19,6 +19,8 @@ from sfa.rspecs.version_manager import VersionManager from sfa.dummy.dummyxrn import DummyXrn, hostname_to_urn, hrn_to_dummy_slicename, slicename_to_hrn +from sfa.storage.alchemy import dbsession +from sfa.storage.model import SliverAllocation import time class DummyAggregate: @@ -51,76 +53,215 @@ class DummyAggregate: return (slice, slivers) - def get_nodes(self, slice_xrn, slice=None,slivers=[], options={}): - # if we are dealing with a slice that has no node just return - # and empty list - if slice_xrn: - if not slice or 'node_ids' not in slice.keys() or not slice['node_ids']: - return [] + def get_nodes(self, options={}): + filter = {} + nodes = self.driver.shell.GetNodes(filter) + return nodes + + def get_slivers(self, urns, options={}): + slice_names = set() + slice_ids = set() + node_ids = [] + for urn in urns: + xrn = DummyXrn(xrn=urn) + if xrn.type == 'sliver': + # id: slice_id-node_id + try: + sliver_id_parts = xrn.get_sliver_id_parts() + slice_id = int(sliver_id_parts[0]) + node_id = int(sliver_id_parts[1]) + slice_ids.add(slice_id) + node_ids.append(node_id) + except ValueError: + pass + else: + slice_names.add(xrn.dummy_slicename()) filter = {} - if slice and 'node_ids' in slice and slice['node_ids']: - filter['node_ids'] = slice['node_ids'] + if slice_names: + filter['slice_name'] = list(slice_names) + if slice_ids: + filter['slice_id'] = list(slice_ids) + # get slices + slices = self.driver.shell.GetSlices(filter) + if not slices: + return [] + slice = slices[0] + slice['hrn'] = DummyXrn(auth=self.driver.hrn, slicename=slice['slice_name']).hrn + + # get sliver users + users = [] + user_ids = [] + for slice in slices: + user_ids.extend(slice['user_ids']) + if user_ids: + users = self.driver.shell.GetUsers({'user_ids': user_ids}) + + # construct user key info + users_list = [] + for user in users: + name = user['email'][0:user['email'].index('@')] + user = { + 'login': slice['slice_name'], + 'user_urn': Xrn('%s.%s' % (self.driver.hrn, name), type='user').urn, + 'keys': user['keys'] + } + users_list.append(user) + + if node_ids: + node_ids = [node_id for node_id in node_ids if node_id in slice['node_ids']] + slice['node_ids'] = node_ids + nodes_dict = self.get_slice_nodes(slice, options) + slivers = [] + for node in nodes_dict.values(): + node.update(slice) + sliver_hrn = '%s.%s-%s' % (self.driver.hrn, slice['slice_id'], node['node_id']) + node['sliver_id'] = Xrn(sliver_hrn, type='sliver').urn + node['urn'] = node['sliver_id'] + node['services_user'] = users + slivers.append(node) + return slivers + + def node_to_rspec_node(self, node, options={}): + rspec_node = NodeElement() + site=self.driver.testbedInfo + rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['name'], node['hostname']) + rspec_node['component_name'] = node['hostname'] + rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn() + rspec_node['authority_id'] = hrn_to_urn(DummyXrn.site_hrn(self.driver.hrn, site['name']), 'authority+sa') + #distinguish between Shared and Reservable nodes + rspec_node['exclusive'] = 'false' + rspec_node['hardware_types'] = [HardwareType({'name': 'dummy-pc'}), + HardwareType({'name': 'pc'})] + if site['longitude'] and site['latitude']: + location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'}) + rspec_node['location'] = location + return rspec_node + + def sliver_to_rspec_node(self, sliver, sliver_allocations): + rspec_node = self.node_to_rspec_node(sliver) + rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires'])) + # add sliver info + rspec_sliver = Sliver({'sliver_id': sliver['urn'], + 'name': sliver['slice_name'], + 'type': 'dummy-vserver', + 'tags': []}) + rspec_node['sliver_id'] = rspec_sliver['sliver_id'] + if sliver['urn'] in sliver_allocations: + rspec_node['client_id'] = sliver_allocations[sliver['urn']].client_id + if sliver_allocations[sliver['urn']].component_id: + rspec_node['component_id'] = sliver_allocations[sliver['urn']].component_id + rspec_node['slivers'] = [rspec_sliver] + + # slivers always provide the ssh service + login = Login({'authentication': 'ssh-keys', + 'hostname': sliver['hostname'], + 'port':'22', + 'username': sliver['slice_name'], + 'login': sliver['slice_name'] + }) + return rspec_node + + def get_slice_nodes(self, slice, options={}): + nodes_dict = {} + filter = {} + if slice and slice.get('node_ids'): + filter['node_ids'] = slice['node_ids'] + else: + # there are no nodes to look up + return nodes_dict nodes = self.driver.shell.GetNodes(filter) - + for node in nodes: + nodes_dict[node['node_id']] = node + return nodes_dict + + def rspec_node_to_geni_sliver(self, rspec_node, sliver_allocations = {}): + if rspec_node['sliver_id'] in sliver_allocations: + # set sliver allocation and operational status + sliver_allocation = sliver_allocations[rspec_node['sliver_id']] + if sliver_allocation: + allocation_status = sliver_allocation.allocation_state + if allocation_status == 'geni_allocated': + op_status = 'geni_pending_allocation' + elif allocation_status == 'geni_provisioned': + op_status = 'geni_ready' + else: + op_status = 'geni_unknown' + else: + allocation_status = 'geni_unallocated' + else: + allocation_status = 'geni_unallocated' + op_status = 'geni_failed' + # required fields + geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'], + 'geni_expires': rspec_node['expires'], + 'geni_allocation_status' : allocation_status, + 'geni_operational_status': op_status, + 'geni_error': '', + } + return geni_sliver + + def list_resources(self, version = None, options={}): + + version_manager = VersionManager() + version = version_manager.get_version(version) + rspec_version = version_manager._get_version(version.type, version.version, 'ad') + rspec = RSpec(version=rspec_version, user_options=options) + + # get nodes + nodes = self.get_nodes(options) + nodes_dict = {} + for node in nodes: + nodes_dict[node['node_id']] = node + + # convert nodes to rspec nodes rspec_nodes = [] for node in nodes: - rspec_node = NodeElement() - # xxx how to retrieve site['login_base'] - site=self.driver.testbedInfo - rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['name'], node['hostname']) - rspec_node['component_name'] = node['hostname'] - rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn() - rspec_node['authority_id'] = hrn_to_urn(DummyXrn.site_hrn(self.driver.hrn, site['name']), 'authority+sa') - rspec_node['exclusive'] = 'false' - rspec_node['hardware_types'] = [HardwareType({'name': 'plab-pc'}), - HardwareType({'name': 'pc'})] - # add site/interface info to nodes. - # assumes that sites, interfaces and tags have already been prepared. - if site['longitude'] and site['latitude']: - location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'}) - rspec_node['location'] = location - - if node['node_id'] in slivers: - # add sliver info - sliver = slivers[node['node_id']] - rspec_node['client_id'] = node['hostname'] - rspec_node['slivers'] = [sliver] - - # slivers always provide the ssh service - login = Login({'authentication': 'ssh-keys', 'hostname': node['hostname'], 'port':'22', 'username': slice['slice_name']}) - service = ServicesElement({'login': login}) - rspec_node['services'] = [service] + rspec_node = self.node_to_rspec_node(node) rspec_nodes.append(rspec_node) - return rspec_nodes - + rspec.version.add_nodes(rspec_nodes) - - def get_rspec(self, slice_xrn=None, version = None, options={}): + return rspec.toxml() + def describe(self, urns, version=None, options={}): version_manager = VersionManager() version = version_manager.get_version(version) - if not slice_xrn: - rspec_version = version_manager._get_version(version.type, version.version, 'ad') + rspec_version = version_manager._get_version(version.type, version.version, 'manifest') + rspec = RSpec(version=rspec_version, user_options=options) + + # get slivers + geni_slivers = [] + slivers = self.get_slivers(urns, options) + if slivers: + rspec_expires = datetime_to_string(utcparse(slivers[0]['expires'])) else: - rspec_version = version_manager._get_version(version.type, version.version, 'manifest') + rspec_expires = datetime_to_string(utcparse(time.time())) + rspec.xml.set('expires', rspec_expires) - slice, slivers = self.get_slice_and_slivers(slice_xrn) - rspec = RSpec(version=rspec_version, user_options=options) - if slice and 'expires' in slice: - rspec.xml.set('expires', datetime_to_string(utcparse(slice['expires']))) - - nodes = self.get_nodes(slice_xrn, slice, slivers, options) - rspec.version.add_nodes(nodes) - # add sliver defaults - default_sliver = slivers.get(None, []) - if default_sliver: - default_sliver_attribs = default_sliver.get('tags', []) - for attrib in default_sliver_attribs: - logger.info(attrib) - rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value']) - - return rspec.toxml() + # lookup the sliver allocations + geni_urn = urns[0] + sliver_ids = [sliver['sliver_id'] for sliver in slivers] + constraint = SliverAllocation.sliver_id.in_(sliver_ids) + sliver_allocations = dbsession.query(SliverAllocation).filter(constraint) + sliver_allocation_dict = {} + for sliver_allocation in sliver_allocations: + geni_urn = sliver_allocation.slice_urn + sliver_allocation_dict[sliver_allocation.sliver_id] = sliver_allocation + + # add slivers + nodes_dict = {} + for sliver in slivers: + nodes_dict[sliver['node_id']] = sliver + rspec_nodes = [] + for sliver in slivers: + rspec_node = self.sliver_to_rspec_node(sliver, sliver_allocation_dict) + rspec_nodes.append(rspec_node) + geni_sliver = self.rspec_node_to_geni_sliver(rspec_node, sliver_allocation_dict) + geni_slivers.append(geni_sliver) + rspec.version.add_nodes(rspec_nodes) + return {'geni_urn': geni_urn, + 'geni_rspec': rspec.toxml(), + 'geni_slivers': geni_slivers} diff --git a/sfa/dummy/dummydriver.py b/sfa/dummy/dummydriver.py index ab2053d4..0d0514e8 100644 --- a/sfa/dummy/dummydriver.py +++ b/sfa/dummy/dummydriver.py @@ -2,8 +2,8 @@ import time import datetime # from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \ - RecordNotFound, SfaNotImplemented, SliverDoesNotExist - + RecordNotFound, SfaNotImplemented, SliverDoesNotExist, SearchFailed, \ + UnsupportedOperation, Forbidden from sfa.util.sfalogging import logger from sfa.util.defaultdict import defaultdict from sfa.util.sfatime import utcparse, datetime_to_string, datetime_to_epoch @@ -12,11 +12,11 @@ from sfa.util.cache import Cache # one would think the driver should not need to mess with the SFA db, but.. from sfa.storage.alchemy import dbsession -from sfa.storage.model import RegRecord +from sfa.storage.model import RegRecord, SliverAllocation +from sfa.trust.credential import Credential # used to be used in get_ticket #from sfa.trust.sfaticket import SfaTicket - from sfa.rspecs.version_manager import VersionManager from sfa.rspecs.rspec import RSpec @@ -52,6 +52,34 @@ class DummyDriver (Driver): self.shell = DummyShell (config) self.testbedInfo = self.shell.GetTestbedInfo() + def check_sliver_credentials(self, creds, urns): + # build list of cred object hrns + slice_cred_names = [] + for cred in creds: + slice_cred_hrn = Credential(cred=cred).get_gid_object().get_hrn() + slice_cred_names.append(DummyXrn(xrn=slice_cred_hrn).dummy_slicename()) + + # look up slice name of slivers listed in urns arg + slice_ids = [] + for urn in urns: + sliver_id_parts = Xrn(xrn=urn).get_sliver_id_parts() + try: + slice_ids.append(int(sliver_id_parts[0])) + except ValueError: + pass + + if not slice_ids: + raise Forbidden("sliver urn not provided") + + slices = self.shell.GetSlices({'slice_ids': slice_ids}) + sliver_names = [slice['slice_name'] for slice in slices] + + # make sure we have a credential for every specified sliver ierd + for sliver_name in sliver_names: + if sliver_name not in slice_cred_names: + msg = "Valid credential not found for target: %s" % sliver_name + raise Forbidden(msg) + ######################################## ########## registry oriented ######################################## @@ -382,191 +410,141 @@ class DummyDriver (Driver): def testbed_name (self): return "dummy" - # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory def aggregate_version (self): - version_manager = VersionManager() - ad_rspec_versions = [] - request_rspec_versions = [] - for rspec_version in version_manager.versions: - if rspec_version.content_type in ['*', 'ad']: - ad_rspec_versions.append(rspec_version.to_dict()) - if rspec_version.content_type in ['*', 'request']: - request_rspec_versions.append(rspec_version.to_dict()) - return { - 'testbed':self.testbed_name(), - 'geni_request_rspec_versions': request_rspec_versions, - 'geni_ad_rspec_versions': ad_rspec_versions, - } - - def list_slices (self, creds, options): - - slices = self.shell.GetSlices() - slice_hrns = [slicename_to_hrn(self.hrn, slice['slice_name']) for slice in slices] - slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns] - - return slice_urns - - # first 2 args are None in case of resource discovery - def list_resources (self, slice_urn, slice_hrn, creds, options): - - version_manager = VersionManager() - # get the rspec's return format from options - rspec_version = version_manager.get_version(options.get('geni_rspec_version')) - version_string = "rspec_%s" % (rspec_version) - + return {} + + def list_resources (self, version=None, options={}): aggregate = DummyAggregate(self) - rspec = aggregate.get_rspec(slice_xrn=slice_urn, version=rspec_version, - options=options) - + rspec = aggregate.list_resources(version=version, options=options) return rspec - - def sliver_status (self, slice_urn, slice_hrn): - # find out where this slice is currently running - slice_name = hrn_to_dummy_slicename(slice_hrn) - - slices = self.shell.GetSlices({'slice_name': slice_name}) - if len(slices) == 0: - raise SliverDoesNotExist("%s (used %s as slicename internally)" % (slice_hrn, slicename)) - slice = slices[0] - - # report about the local nodes only - nodes = self.shell.GetNodes({'node_ids':slice['node_ids']}) - - if len(nodes) == 0: - raise SliverDoesNotExist("You have not allocated any slivers here") - - # get login info - user = {} - keys = [] - if slice['user_ids']: - users = self.shell.GetUsers({'user_ids': slice['user_ids']}) - for user in users: - keys.extend(user['keys']) - - user.update({'urn': slice_urn, - 'login': slice['slice_name'], - 'protocol': ['ssh'], - 'port': ['22'], - 'keys': keys}) + def describe(self, urns, version, options={}): + aggregate = DummyAggregate(self) + return aggregate.describe(urns, version=version, options=options) - result = {} - top_level_status = 'unknown' - if nodes: - top_level_status = 'ready' - result['geni_urn'] = slice_urn - result['dummy_login'] = slice['slice_name'] - result['dummy_expires'] = datetime_to_string(utcparse(slice['expires'])) - result['geni_expires'] = datetime_to_string(utcparse(slice['expires'])) - - resources = [] - for node in nodes: - res = {} - res['dummy_hostname'] = node['hostname'] - res['geni_expires'] = datetime_to_string(utcparse(slice['expires'])) - sliver_id = Xrn(slice_urn, type='slice', id=node['node_id']).urn - res['geni_urn'] = sliver_id - res['geni_status'] = 'ready' - res['geni_error'] = '' - res['users'] = [users] - - resources.append(res) - - result['geni_status'] = top_level_status - result['geni_resources'] = resources - return result - - def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, users, options): + def status (self, urns, options={}): + aggregate = DummyAggregate(self) + desc = aggregate.describe(urns, version='GENI 3') + status = {'geni_urn': desc['geni_urn'], + 'geni_slivers': desc['geni_slivers']} + return status + + def allocate (self, urn, rspec_string, expiration, options={}): + xrn = Xrn(urn) aggregate = DummyAggregate(self) slices = DummySlices(self) - sfa_peer = slices.get_sfa_peer(slice_hrn) - slice_record=None + slice_record=None + users = options.get('geni_users', []) if users: slice_record = users[0].get('slice_record', {}) - + # parse rspec rspec = RSpec(rspec_string) requested_attributes = rspec.version.get_slice_attributes() - + # ensure slice record exists - slice = slices.verify_slice(slice_hrn, slice_record, sfa_peer, options=options) - # ensure user records exists - #users = slices.verify_users(slice_hrn, slice, users, sfa_peer, options=options) - + slice = slices.verify_slice(xrn.hrn, slice_record, expiration=expiration, options=options) + # ensure person records exists + #persons = slices.verify_persons(xrn.hrn, slice, users, peer, sfa_peer, options=options) + # add/remove slice from nodes - requested_slivers = [] - for node in rspec.version.get_nodes_with_slivers(): - hostname = None - if node.get('component_name'): - hostname = node.get('component_name').strip() - elif node.get('component_id'): - hostname = xrn_to_hostname(node.get('component_id').strip()) - if hostname: - requested_slivers.append(hostname) - requested_slivers_ids = [] - for hostname in requested_slivers: - node_id = self.shell.GetNodes({'hostname': hostname})[0]['node_id'] - requested_slivers_ids.append(node_id) - nodes = slices.verify_slice_nodes(slice, requested_slivers_ids) - - return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version) + request_nodes = rspec.version.get_nodes_with_slivers() + nodes = slices.verify_slice_nodes(urn, slice, request_nodes) - def delete_sliver (self, slice_urn, slice_hrn, creds, options): - slicename = hrn_to_dummy_slicename(slice_hrn) - slices = self.shell.GetSlices({'slice_name': slicename}) - if not slices: - return True - slice = slices[0] - - try: - self.shell.DeleteSliceFromNodes({'slice_id': slice['slice_id'], 'node_ids': slice['node_ids']}) - return True - except: - return False - - def renew_sliver (self, slice_urn, slice_hrn, creds, expiration_time, options): - slicename = hrn_to_dummy_slicename(slice_hrn) - slices = self.shell.GetSlices({'slice_name': slicename}) - if not slices: - raise RecordNotFound(slice_hrn) - slice = slices[0] + return aggregate.describe([xrn.get_urn()], version=rspec.version) + + def provision(self, urns, options={}): + # update users + slices = DummySlices(self) + aggregate = DummyAggregate(self) + slivers = aggregate.get_slivers(urns) + slice = slivers[0] + geni_users = options.get('geni_users', []) + #users = slices.verify_users(None, slice, geni_users, options=options) + # update sliver allocation states and set them to geni_provisioned + sliver_ids = [sliver['sliver_id'] for sliver in slivers] + SliverAllocation.set_allocations(sliver_ids, 'geni_provisioned') + version_manager = VersionManager() + rspec_version = version_manager.get_version(options['geni_rspec_version']) + return self.describe(urns, rspec_version, options=options) + + def delete(self, urns, options={}): + # collect sliver ids so we can update sliver allocation states after + # we remove the slivers. + aggregate = DummyAggregate(self) + slivers = aggregate.get_slivers(urns) + if slivers: + slice_id = slivers[0]['slice_id'] + node_ids = [] + sliver_ids = [] + for sliver in slivers: + node_ids.append(sliver['node_id']) + sliver_ids.append(sliver['sliver_id']) + + # determine if this is a peer slice + # xxx I wonder if this would not need to use PlSlices.get_peer instead + # in which case plc.peers could be deprecated as this here + # is the only/last call to this last method in plc.peers + slice_hrn = DummyXrn(auth=self.hrn, slicename=slivers[0]['slice_name']).get_hrn() + try: + self.shell.DeleteSliceFromNodes({'slice_id': slice_id, 'node_ids': node_ids}) + # delete sliver allocation states + SliverAllocation.delete_allocations(sliver_ids) + finally: + pass + + # prepare return struct + geni_slivers = [] + for sliver in slivers: + geni_slivers.append( + {'geni_sliver_urn': sliver['sliver_id'], + 'geni_allocation_status': 'geni_unallocated', + 'geni_expires': datetime_to_string(utcparse(sliver['expires']))}) + return geni_slivers + + def renew (self, urns, expiration_time, options={}): + aggregate = DummyAggregate(self) + slivers = aggregate.get_slivers(urns) + if not slivers: + raise SearchFailed(urns) + slice = slivers[0] requested_time = utcparse(expiration_time) record = {'expires': int(datetime_to_epoch(requested_time))} - try: - self.shell.UpdateSlice({'slice_id': slice['slice_id'], 'fields':record}) - return True - except: - return False - - # set the 'enabled' tag to True - def start_slice (self, slice_urn, slice_hrn, creds): - slicename = hrn_to_dummy_slicename(slice_hrn) - slices = self.shell.GetSlices({'slice_name': slicename}) - if not slices: - raise RecordNotFound(slice_hrn) - slice_id = slices[0]['slice_id'] - slice_enabled = slices[0]['enabled'] - # just update the slice enabled tag - if not slice_enabled: - self.shell.UpdateSlice({'slice_id': slice_id, 'fields': {'enabled': True}}) - return 1 - - # set the 'enabled' tag to False - def stop_slice (self, slice_urn, slice_hrn, creds): - slicename = hrn_to_pl_slicename(slice_hrn) - slices = self.shell.GetSlices({'slice_name': slicename}) + self.shell.UpdateSlice({'slice_id': slice['slice_id'], 'fileds': record}) + description = self.describe(urns, 'GENI 3', options) + return description['geni_slivers'] + + def perform_operational_action (self, urns, action, options={}): + # Dummy doesn't support operational actions. Lets pretend like it + # supports start, but reject everything else. + action = action.lower() + if action not in ['geni_start']: + raise UnsupportedOperation(action) + + # fault if sliver is not full allocated (operational status is geni_pending_allocation) + description = self.describe(urns, 'GENI 3', options) + for sliver in description['geni_slivers']: + if sliver['geni_operational_status'] == 'geni_pending_allocation': + raise UnsupportedOperation(action, "Sliver must be fully allocated (operational status is not geni_pending_allocation)") + # + # Perform Operational Action Here + # + + geni_slivers = self.describe(urns, 'GENI 3', options)['geni_slivers'] + return geni_slivers + + def shutdown (self, xrn, options={}): + xrn = DummyXrn(xrn=xrn, type='slice') + slicename = xrn.pl_slicename() + slices = self.shell.GetSlices({'name': slicename}, ['slice_id']) if not slices: raise RecordNotFound(slice_hrn) slice_id = slices[0]['slice_id'] - slice_enabled = slices[0]['enabled'] - # just update the slice enabled tag - if slice_enabled: - self.shell.UpdateSlice({'slice_id': slice_id, 'fields': {'enabled': False}}) + slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'}) + if not slice_tags: + self.shell.AddSliceTag(slice_id, 'enabled', '0') + elif slice_tags[0]['value'] != "0": + tag_id = slice_tags[0]['slice_tag_id'] + self.shell.UpdateSliceTag(tag_id, '0') return 1 - - def reset_slice (self, slice_urn, slice_hrn, creds): - raise SfaNotImplemented ("reset_slice not available at this interface") - - def get_ticket (self, slice_urn, slice_hrn, creds, rspec_string, options): - raise SfaNotImplemented,"DummyDriver.get_ticket needs a rewrite" diff --git a/sfa/dummy/dummyslices.py b/sfa/dummy/dummyslices.py index d334aa2a..dddf1a66 100644 --- a/sfa/dummy/dummyslices.py +++ b/sfa/dummy/dummyslices.py @@ -1,3 +1,4 @@ +import time from types import StringTypes from collections import defaultdict @@ -6,6 +7,8 @@ from sfa.util.sfalogging import logger from sfa.util.xrn import Xrn, get_leaf, get_authority, urn_to_hrn from sfa.rspecs.rspec import RSpec +from sfa.storage.model import SliverAllocation +from sfa.storage.alchemy import dbsession from sfa.dummy.dummyxrn import DummyXrn, hrn_to_dummy_slicename @@ -13,7 +16,6 @@ MAXINT = 2L**31-1 class DummySlices: - def __init__(self, driver): self.driver = driver @@ -54,21 +56,26 @@ class DummySlices: return slivers - def get_sfa_peer(self, xrn): - hrn, type = urn_to_hrn(xrn) - - # return the authority for this hrn or None if we are the authority - sfa_peer = None - slice_authority = get_authority(hrn) - site_authority = get_authority(slice_authority) - if site_authority != self.driver.hrn: - sfa_peer = site_authority + def verify_slice_nodes(self, slice_urn, slice, rspec_nodes): + + slivers = {} + for node in rspec_nodes: + hostname = node.get('component_name') + client_id = node.get('client_id') + component_id = node.get('component_id').strip() + if hostname: + hostname = hostname.strip() + elif component_id: + hostname = xrn_to_hostname(component_id) + if hostname: + slivers[hostname] = {'client_id': client_id, 'component_id': component_id} + all_nodes = self.driver.shell.GetNodes() + requested_slivers = [] + for node in all_nodes: + if node['hostname'] in slivers.keys(): + requested_slivers.append(node['node_id']) - return sfa_peer - - - def verify_slice_nodes(self, slice, requested_slivers): if 'node_ids' not in slice.keys(): slice['node_ids']=[] nodes = self.driver.shell.GetNodes({'node_ids': slice['node_ids']}) @@ -86,11 +93,25 @@ class DummySlices: except: logger.log_exc('Failed to add/remove slice from nodes') - return nodes + slices = self.driver.shell.GetSlices({'slice_name': slice['slice_name']}) + resulting_nodes = self.driver.shell.GetNodes({'node_ids': slices[0]['node_ids']}) + + # update sliver allocations + for node in resulting_nodes: + client_id = slivers[node['hostname']]['client_id'] + component_id = slivers[node['hostname']]['component_id'] + sliver_hrn = '%s.%s-%s' % (self.driver.hrn, slice['slice_id'], node['node_id']) + sliver_id = Xrn(sliver_hrn, type='sliver').urn + record = SliverAllocation(sliver_id=sliver_id, client_id=client_id, + component_id=component_id, + slice_urn = slice_urn, + allocation_state='geni_allocated') + record.sync() + return resulting_nodes - def verify_slice(self, slice_hrn, slice_record, sfa_peer, options={}): + def verify_slice(self, slice_hrn, slice_record, expiration, options={}): slicename = hrn_to_dummy_slicename(slice_hrn) parts = slicename.split("_") login_base = parts[0] @@ -106,148 +127,40 @@ class DummySlices: if slice_record and slice_record.get('expires'): requested_expires = int(datetime_to_epoch(utcparse(slice_record['expires']))) if requested_expires and slice['expires'] != requested_expires: - self.driver.shell.UpdateSlice( {'slice_id': slice['slice_id'], 'fields':{'expires' : requested_expires}}) + self.driver.shell.UpdateSlice( {'slice_id': slice['slice_id'], 'fields':{'expires' : expiration}}) return slice - def verify_users(self, slice_hrn, slice_record, users, sfa_peer, options={}): + def verify_users(self, slice_hrn, slice_record, users, options={}): + slice_name = hrn_to_dummy_slicename(slice_hrn) users_by_email = {} - users_dict = {} - users_by_site = {} for user in users: user['urn'] = user['urn'].lower() hrn, type = urn_to_hrn(user['urn']) username = get_leaf(hrn) - login_base = DummyXrn(xrn=user['urn']).dummy_login_base() user['username'] = username - user['site'] = login_base if 'email' in user: user['email'] = user['email'].lower() users_by_email[user['email']] = user - users_dict[user['email']] = user - else: - users_by_site[user['site']].append(user) - + # start building a list of existing users - existing_user_ids = [] - existing_user_ids_filter = [] - if users_by_email: - existing_user_ids_filter.extend(users_by_email.keys()) - if users_by_site: - for login_base in users_by_site: - users = users_by_site[login_base] - for user in users: - existing_user_ids_filter.append(user['username']+'@geni.net') - if existing_user_ids_filter: - # get existing users by email - existing_users = self.driver.shell.GetPersons({'email': existing_user_ids_filter}, - ['person_id', 'key_ids', 'email']) - existing_user_ids.extend([user['email'] for user in existing_users]) - - if users_by_site: - # get a list of user sites (based on requeste user urns - site_list = self.driver.shell.GetSites(users_by_site.keys(), \ - ['site_id', 'login_base', 'person_ids']) - # get all existing users at these sites - sites = {} - site_user_ids = [] - for site in site_list: - sites[site['site_id']] = site - site_user_ids.extend(site['person_ids']) - - existing_site_persons_list = self.driver.shell.GetPersons(site_user_ids, - ['person_id', 'key_ids', 'email', 'site_ids']) - - # all requested users are either existing users or new (added) users - for login_base in users_by_site: - requested_site_users = users_by_site[login_base] - for requested_user in requested_site_users: - user_found = False - for existing_user in existing_site_persons_list: - for site_id in existing_user['site_ids']: - if site_id in sites: - site = sites[site_id] - if login_base == site['login_base'] and \ - existing_user['email'].startswith(requested_user['username']+'@'): - existing_user_ids.append(existing_user['email']) - requested_user['email'] = existing_user['email'] - users_dict[existing_user['email']] = requested_user - user_found = True - break - if user_found: - break - - if user_found == False: - fake_email = requested_user['username'] + '@geni.net' - requested_user['email'] = fake_email - users_dict[fake_email] = requested_user + existing_users_by_email = {} + existing_slice_users_by_email = {} + existing_users = self.driver.shell.GetUsers() + existing_slice_users_ids = self.driver.shell.GetSlices({'slice_name': slice_name})[0]['user_ids'] + for user in existing_users: + existing_users_by_email[user['email']] = user + if user['user_id'] in existing_slice_users_ids: + existing_slice_users_by_email[user['email']] = user - # requested slice users - requested_user_ids = users_dict.keys() - # existing slice users - existing_slice_users_filter = {'person_id': slice_record.get('person_ids', [])} - existing_slice_users = self.driver.shell.GetPersons(existing_slice_users_filter, - ['person_id', 'key_ids', 'email']) - existing_slice_user_ids = [user['email'] for user in existing_slice_users] - - # users to be added, removed or updated - added_user_ids = set(requested_user_ids).difference(existing_user_ids) - added_slice_user_ids = set(requested_user_ids).difference(existing_slice_user_ids) - removed_user_ids = set(existing_slice_user_ids).difference(requested_user_ids) - updated_user_ids = set(existing_slice_user_ids).intersection(requested_user_ids) - - # Remove stale users (only if we are not appending). - # Append by default. - append = options.get('append', True) - if append == False: - for removed_user_id in removed_user_ids: - self.driver.shell.DeletePersonFromSlice(removed_user_id, slice_record['name']) - # update_existing users - updated_users_list = [user for user in users_dict.values() if user['email'] in \ - updated_user_ids] - self.verify_keys(existing_slice_users, updated_users_list, options) - - added_persons = [] - # add new users - for added_user_id in added_user_ids: - added_user = users_dict[added_user_id] - hrn, type = urn_to_hrn(added_user['urn']) - person = { - 'first_name': added_user.get('first_name', hrn), - 'last_name': added_user.get('last_name', hrn), - 'email': added_user_id, - 'peer_person_id': None, - 'keys': [], - 'key_ids': added_user.get('key_ids', []), - } - person['person_id'] = self.driver.shell.AddPerson(person) - added_persons.append(person) - - # enable the account - self.driver.shell.UpdatePerson(person['person_id'], {'enabled': True}) - - # add person to site - self.driver.shell.AddPersonToSite(added_user_id, added_user['site']) - - for key_string in added_user.get('keys', []): - key = {'key':key_string, 'key_type':'ssh'} - key['key_id'] = self.driver.shell.AddPersonKey(person['person_id'], key) - person['keys'].append(key) - - # add the registry record -# if sfa_peer: -# peer_dict = {'type': 'user', 'hrn': hrn, 'peer_authority': sfa_peer, \ -# 'pointer': person['person_id']} -# self.registry.register_peer_object(self.credential, peer_dict) - - for added_slice_user_id in added_slice_user_ids.union(added_user_ids): - # add person to the slice - self.driver.shell.AddPersonToSlice(added_slice_user_id, slice_record['name']) - # if this is a peer record then it should already be bound to a peer. - # no need to return worry about it getting bound later - - return added_persons + add_users_by_email = set(users_by_email).difference(existing_slice_user_by_email) + delete_users_by_email = set(existing_slice_user_by_email).difference(users_by_email) + try: + for user in add_users_by_email: + self.driver.shell.AddUser() + except: + pass def verify_keys(self, old_users, new_users, options={}): @@ -284,4 +197,3 @@ class DummySlices: except: pass -