# -*- coding:utf-8 -*- """ aggregate class management """ from sfa.util.xrn import Xrn, hrn_to_urn from sfa.util.sfatime import utcparse, datetime_to_string from sfa.util.sfalogging import logger from sfa.rspecs.rspec import RSpec from sfa.rspecs.elements.hardware_type import HardwareType from sfa.rspecs.elements.lease import Lease from sfa.rspecs.elements.granularity import Granularity from sfa.rspecs.version_manager import VersionManager from sfa.rspecs.elements.services import ServicesElement from sfa.rspecs.elements.login import Login from sfa.rspecs.elements.sliver import Sliver from sfa.rspecs.elements.versions.iotlabv1Node import IotlabPosition from sfa.rspecs.elements.versions.iotlabv1Node import IotlabNode from sfa.rspecs.elements.versions.iotlabv1Node import IotlabLocation from sfa.iotlab.iotlablease import LeaseTable import time import datetime class IotLABAggregate(object): """ SFA aggregate for Iot-LAB testbed """ def __init__(self, driver): self.driver = driver def leases_to_rspec_leases(self, leases): """ Get leases attributes list""" rspec_leases = [] for lease in leases: for node in lease['resources']: rspec_lease = Lease() rspec_lease['lease_id'] = lease['id'] iotlab_xrn = Xrn('.'.join([self.driver.root_auth, Xrn.escape(node)]), type='node') rspec_lease['component_id'] = iotlab_xrn.urn rspec_lease['start_time'] = str(lease['date']) # duration in minutes duration = int(lease['duration']) / 60 rspec_lease['duration'] = duration rspec_lease['slice_id'] = lease['slice_id'] rspec_leases.append(rspec_lease) return rspec_leases def node_to_rspec_node(self, node): """ Get node attributes """ rspec_node = IotlabNode() rspec_node['mobile'] = node['mobile'] rspec_node['archi'] = node['archi'] if ':' in node['archi']: rspec_node['radio'] = (node['archi'].split(':'))[1] else: rspec_node['radio'] = node['archi'] iotlab_xrn = Xrn('.'.join([self.driver.root_auth, Xrn.escape(node['network_address'])]), type='node') # rspec_node['boot_state'] = 'true' if node['state'] == 'Absent' or \ node['state'] == 'Suspected' or \ node['state'] == 'Dead' or \ node['state'] == 'Busy': rspec_node['available'] = 'false' else: rspec_node['available'] = 'true' rspec_node['component_id'] = iotlab_xrn.urn rspec_node['component_name'] = node['network_address'] rspec_node['component_manager_id'] = hrn_to_urn(self.driver.root_auth, 'authority+sa') rspec_node['authority_id'] = rspec_node['component_manager_id'] rspec_node['exclusive'] = 'true' rspec_node['hardware_types'] = \ [HardwareType({'name': node['archi']})] location = IotlabLocation({'country': 'France', 'site': node['site']}) rspec_node['location'] = location position = IotlabPosition() for field in position: position[field] = node[field] granularity = Granularity({'grain': 30}) rspec_node['granularity'] = granularity rspec_node['tags'] = [] return rspec_node def sliver_to_rspec_node(self, sliver): """ Get node and sliver attributes """ rspec_node = self.node_to_rspec_node(sliver) rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires'])) rspec_node['sliver_id'] = sliver['sliver_id'] rspec_sliver = Sliver({'sliver_id': sliver['sliver_id'], 'name': sliver['name'], 'type': sliver['archi'], 'tags': []}) rspec_node['slivers'] = [rspec_sliver] # slivers always provide the ssh service login = Login({'authentication': 'ssh-keys', 'hostname': sliver['hostname'], 'port': '22', 'username': sliver['name'], 'login': sliver['name'] }) service = ServicesElement({'login': login}) rspec_node['services'] = [service] return rspec_node @classmethod def rspec_node_to_geni_sliver(cls, rspec_node): """ Get sliver status """ geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'], 'geni_expires': rspec_node['expires'], 'geni_allocation_status': 'geni_allocated', 'geni_operational_status': 'geni_pending_allocation', 'geni_error': '', } return geni_sliver def list_resources(self, version=None, options=None): """ list_resources method sends a RSpec with all Iot-LAB testbed nodes and leases (OAR job submission). For leases we get all OAR jobs with state Waiting or Running. If we have an entry in SFA database (lease table) with OAR job id this submission was launched by SFA driver, otherwise it was launched by Iot-LAB Webportal or CLI-tools :Example: ... ... ... ... """ # pylint:disable=R0914,W0212 logger.warning("iotlabaggregate list_resources") logger.warning("iotlabaggregate list_resources options %s" % options) if not options: 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) nodes = self.driver.shell.get_nodes() reserved_nodes = self.driver.shell.get_reserved_nodes() if 'error' not in nodes and 'error' not in reserved_nodes: # convert nodes to rspec nodes rspec_nodes = [] for node in nodes: rspec_node = self.node_to_rspec_node(nodes[node]) rspec_nodes.append(rspec_node) rspec.version.add_nodes(rspec_nodes) leases = [] db_leases = {} # find OAR jobs id for all slices in SFA database for lease in self.driver.api.dbsession().query(LeaseTable).all(): db_leases[lease.job_id] = lease.slice_hrn for lease_id in reserved_nodes: # onelab slice = job submission from OneLAB if lease_id in db_leases: reserved_nodes[lease_id]['slice_id'] = \ hrn_to_urn(db_leases[lease_id], 'slice') # iotlab slice = job submission from Iot-LAB else: reserved_nodes[lease_id]['slice_id'] = \ hrn_to_urn(self.driver.root_auth + '.' + reserved_nodes[lease_id][ 'owner'] + "_slice", 'slice') leases.append(reserved_nodes[lease_id]) rspec_leases = self.leases_to_rspec_leases(leases) rspec.version.add_leases(rspec_leases) return rspec.toxml() def get_slivers(self, urns, leases, nodes): """ Get slivers attributes list """ logger.warning("iotlabaggregate get_slivers") logger.warning("iotlabaggregate get_slivers urns %s" % urns) slivers = [] for lease in leases: for node in lease['resources']: network_address = node.split(".") sliver_node = nodes[node] sliver_hrn = '%s.%s-%s' % (self.driver.hrn, lease['id'], network_address[0]) start_time = datetime.datetime.fromtimestamp(lease['date']) duration = datetime.timedelta(seconds=int(lease['duration'])) sliver_node['expires'] = start_time + duration sliver_node['sliver_id'] = Xrn(sliver_hrn, type='sliver').urn # frontend SSH hostname sliver_node['hostname'] = '.'.join(network_address[1:]) # user login sliver_node['name'] = lease['owner'] slivers.append(sliver_node) return slivers def _delete_db_lease(self, job_id): """ Delete lease table row in SFA database """ logger.warning("iotlabdriver _delete_db_lease lease job_id : %s" % job_id) self.driver.api.dbsession().query(LeaseTable).filter( LeaseTable.job_id == job_id).delete() self.driver.api.dbsession().commit() def describe(self, urns, version=None, options=None): """ describe method returns slice slivers (allocated resources) and leases (OAR job submission). We search in lease table of SFA database all OAR jobs id for this slice and match OAR jobs with state Waiting or Running. If OAR job id doesn't exist the experiment is terminated and we delete the database table entry. Otherwise we add slivers and leases in the response :returns: geni_slivers : a list of allocated slivers with information about their allocation and operational state geni_urn : the URN of the slice in which the sliver has been allocated geni_rspec: a RSpec describing the allocated slivers and leases :rtype: dict :Example: ... ... """ # pylint:disable=R0914,W0212 logger.warning("iotlabaggregate describe") logger.warning("iotlabaggregate describe urns : %s" % urns) if not options: options = {} version_manager = VersionManager() version = version_manager.get_version(version) rspec_version = version_manager._get_version(version.type, version.version, 'manifest') rspec = RSpec(version=rspec_version, user_options=options) xrn = Xrn(urns[0]) geni_slivers = [] nodes = self.driver.shell.get_nodes() reserved_nodes = self.driver.shell.get_reserved_nodes() if 'error' not in nodes and 'error' not in reserved_nodes: # find OAR jobs id for one slice in SFA database db_leases = [(lease.job_id, lease.slice_hrn) for lease in self.driver.api.dbsession() .query(LeaseTable) .filter(LeaseTable.slice_hrn == xrn.hrn).all()] leases = [] for job_id, slice_hrn in db_leases: # OAR job terminated, we delete entry in database if job_id not in reserved_nodes: self._delete_db_lease(job_id) else: # onelab slice = job submission from OneLAB lease = reserved_nodes[job_id] lease['slice_id'] = hrn_to_urn(slice_hrn, 'slice') leases.append(lease) # get slivers slivers = self.get_slivers(urns, leases, nodes) if slivers: date = utcparse(slivers[0]['expires']) rspec_expires = datetime_to_string(date) else: rspec_expires = datetime_to_string(utcparse(time.time())) rspec.xml.set('expires', rspec_expires) rspec_nodes = [] for sliver in slivers: rspec_node = self.sliver_to_rspec_node(sliver) rspec_nodes.append(rspec_node) geni_sliver = self.rspec_node_to_geni_sliver(rspec_node) geni_slivers.append(geni_sliver) logger.warning("iotlabaggregate describe geni_slivers %s" % geni_slivers) rspec.version.add_nodes(rspec_nodes) rspec_leases = self.leases_to_rspec_leases(leases) logger.warning("iotlabaggregate describe rspec_leases %s" % rspec_leases) rspec.version.add_leases(rspec_leases) return {'geni_urn': urns[0], 'geni_rspec': rspec.toxml(), 'geni_slivers': geni_slivers}