delete cortexlab driver (obsolete) + write again Iot-LAB SFA driver with Iot-LAB...
[sfa.git] / sfa / iotlab / iotlabaggregate.py
1 # -*- coding:utf-8 -*-
2 """ aggregate class management """
3
4 from sfa.util.xrn import Xrn, hrn_to_urn
5 from sfa.util.sfatime import utcparse, datetime_to_string
6 from sfa.util.sfalogging import logger
7 from sfa.rspecs.rspec import RSpec
8 from sfa.rspecs.elements.hardware_type import HardwareType
9 from sfa.rspecs.elements.lease import Lease
10 from sfa.rspecs.elements.granularity import Granularity
11 from sfa.rspecs.version_manager import VersionManager
12 from sfa.rspecs.elements.versions.iotlabv1Node import IotlabPosition
13 from sfa.rspecs.elements.versions.iotlabv1Node import IotlabNode
14 from sfa.rspecs.elements.versions.iotlabv1Node import IotlabLocation
15 from sfa.iotlab.iotlablease import LeaseTable
16 import time
17 import datetime
18
19 class IotLABAggregate(object):
20     """
21     SFA aggregate for Iot-LAB testbed
22     """
23
24     def __init__(self, driver):
25         self.driver = driver
26
27
28     def leases_to_rspec_leases(self, leases):
29         """ Get leases attributes list"""
30         rspec_leases = []
31         for lease in leases:
32             for node in lease['resources']:
33                 rspec_lease = Lease()
34                 rspec_lease['lease_id'] = lease['id']
35                 iotlab_xrn = Xrn('.'.join([self.driver.root_auth,
36                               Xrn.escape(node)]),
37                               type='node')
38                 rspec_lease['component_id'] = iotlab_xrn.urn
39                 rspec_lease['start_time'] = str(lease['date'])
40                 duration = int(lease['duration'])/60 # duration in minutes
41                 rspec_lease['duration'] = duration
42                 rspec_lease['slice_id'] = lease['slice_id']
43                 rspec_leases.append(rspec_lease)
44         return rspec_leases
45
46
47     def node_to_rspec_node(self, node):
48         """ Get node attributes """
49         rspec_node = IotlabNode()
50         rspec_node['mobile'] = node['mobile']
51         rspec_node['archi'] = node['archi']
52         rspec_node['radio'] = (node['archi'].split(':'))[1]
53         iotlab_xrn = Xrn('.'.join([self.driver.root_auth,
54                               Xrn.escape(node['network_address'])]),
55                               type='node')
56         rspec_node['boot_state'] = 'true'
57         rspec_node['component_id'] = iotlab_xrn.urn
58         rspec_node['component_name'] = node['network_address']
59         rspec_node['component_manager_id'] = \
60                         hrn_to_urn(self.driver.root_auth,
61                         'authority+sa')
62         rspec_node['authority_id'] = rspec_node['component_manager_id']
63         rspec_node['exclusive'] = 'true'
64         rspec_node['hardware_types'] = [HardwareType({'name': \
65                                         'iotlab-node'})]
66         location = IotlabLocation({'country':'France', 'site': \
67                                     node['site']})
68         rspec_node['location'] = location
69         position = IotlabPosition()
70         for field in position:
71             position[field] = node[field]
72         granularity = Granularity({'grain': 30})
73         rspec_node['granularity'] = granularity
74         rspec_node['tags'] = []
75         return rspec_node
76
77
78     def sliver_to_rspec_node(self, sliver):
79         """ Get node and sliver attributes """
80         rspec_node = self.node_to_rspec_node(sliver)
81         rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires']))
82         rspec_node['sliver_id'] = sliver['sliver_id']
83         return rspec_node
84
85
86     @classmethod
87     def rspec_node_to_geni_sliver(cls, rspec_node):
88         """ Get sliver status """
89         geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'],
90                        'geni_expires': rspec_node['expires'],
91                        'geni_allocation_status' : 'geni_allocated',
92                        'geni_operational_status': 'geni_pending_allocation',
93                        'geni_error': '',
94                        }
95         return geni_sliver
96
97
98     def list_resources(self, version=None, options=None):
99         """
100         list_resources method sends a RSpec with all Iot-LAB testbed nodes
101         and leases (OAR job submission). For leases we get all OAR jobs with
102         state Waiting or Running. If we have an entry in SFA database
103         (lease table) with OAR job id this submission was launched by SFA
104         driver, otherwise it was launched by Iot-LAB Webportal or CLI-tools
105
106         :Example:
107         <rspec>
108         ...
109         <node component_manager_id="urn:publicid:IDN+iotlab+authority+sa"
110               component_id=
111                   "urn:publicid:IDN+iotlab+node+m3-10.devgrenoble.iot-lab.info"
112               exclusive="true" component_name="m3-10.devgrenoble.iot-lab.info">
113             <hardware_type name="iotlab-node"/>
114             <location country="France"/>
115             <granularity grain="60"/>
116             ...
117         </node>
118         ...
119         <lease slice_id="urn:publicid:IDN+onelab:inria+slice+test_iotlab"
120             start_time="1427792400" duration="30">
121             <node component_id=
122                 "urn:publicid:IDN+iotlab+node+m3-10.grenoble.iot-lab.info"/>
123         </lease>
124         ...
125         </rspec>
126         """
127         # pylint:disable=R0914,W0212
128         logger.warning("iotlabaggregate list_resources")
129         logger.warning("iotlabaggregate list_resources options %s" % options)
130         if not options:
131             options = {}
132
133         version_manager = VersionManager()
134         version = version_manager.get_version(version)
135         rspec_version = version_manager._get_version(version.type,
136                                                      version.version,
137                                                      'ad')
138         rspec = RSpec(version=rspec_version, user_options=options)
139
140         nodes = self.driver.shell.get_nodes()
141         reserved_nodes = self.driver.shell.get_reserved_nodes()
142         if not 'error' in nodes and not 'error' in reserved_nodes:
143             # convert nodes to rspec nodes
144             rspec_nodes = []
145             for node in nodes:
146                 rspec_node = self.node_to_rspec_node(nodes[node])
147                 rspec_nodes.append(rspec_node)
148             rspec.version.add_nodes(rspec_nodes)
149
150             leases = []
151             db_leases = {}
152             # find OAR jobs id for all slices in SFA database
153             for lease in self.driver.api.dbsession().query(LeaseTable).all():
154                 db_leases[lease.job_id] = lease.slice_hrn
155
156             for lease_id in reserved_nodes:
157                 # onelab slice = job submission from OneLAB
158                 if lease_id in db_leases:
159                     reserved_nodes[lease_id]['slice_id'] = \
160                         hrn_to_urn(db_leases[lease_id],
161                                    'slice')
162                 # iotlab slice = job submission from Iot-LAB
163                 else:
164                     reserved_nodes[lease_id]['slice_id'] = \
165                         hrn_to_urn(self.driver.root_auth+'.'+
166                                    reserved_nodes[lease_id]['owner']+"_slice",
167                                    'slice')
168                 leases.append(reserved_nodes[lease_id])
169
170             rspec_leases = self.leases_to_rspec_leases(leases)
171             logger.warning("iotlabaggregate list_resources rspec_leases  %s" %
172                            rspec_leases)
173             rspec.version.add_leases(rspec_leases)
174         return rspec.toxml()
175
176
177     def get_slivers(self, urns, leases, nodes):
178         """ Get slivers attributes list """
179         logger.warning("iotlabaggregate get_slivers")
180         logger.warning("iotlabaggregate get_slivers urns %s" % urns)
181         slivers = []
182         for lease in leases:
183             for node in lease['resources']:
184                 sliver_node = nodes[node]
185                 sliver_hrn = '%s.%s-%s' % (self.driver.hrn,
186                              lease['id'], node.split(".")[0])
187                 start_time = datetime.datetime.fromtimestamp(lease['date'])
188                 duration = datetime.timedelta(seconds=int(lease['duration']))
189                 sliver_node['expires'] = start_time + duration
190                 sliver_node['sliver_id'] = Xrn(sliver_hrn,
191                                                type='sliver').urn
192                 slivers.append(sliver_node)
193         return slivers
194
195
196     def _delete_db_lease(self, job_id):
197         """ Delete lease table row in SFA database """
198         logger.warning("iotlabdriver _delete_db_lease lease job_id : %s"
199                        % job_id)
200         self.driver.api.dbsession().query(LeaseTable).filter(
201             LeaseTable.job_id == job_id).delete()
202         self.driver.api.dbsession().commit()
203
204
205     def describe(self, urns, version=None, options=None):
206         """
207         describe method returns slice slivers (allocated resources) and leases
208         (OAR job submission). We search in lease table of SFA database all OAR
209         jobs id for this slice and match OAR jobs with state Waiting or Running.
210         If OAR job id doesn't exist the experiment is terminated and we delete
211         the database table entry. Otherwise we add slivers and leases in the
212         response
213
214         :returns:
215             geni_slivers : a list of allocated slivers with information about
216                            their allocation and operational state
217             geni_urn : the URN of the slice in which the sliver has been
218                        allocated
219             geni_rspec:  a RSpec describing the allocated slivers and leases
220         :rtype: dict
221
222         :Example:
223         <rspec>
224         ...
225         <node component_manager_id="urn:publicid:IDN+iotlab+authority+sa"
226               component_id=
227                   "urn:publicid:IDN+iotlab+node+m3-10.grenoble.iot-lab.info"
228               client_id="m3-10.grenoble.iot-lab.info"
229               sliver_id="urn:publicid:IDN+iotlab+sliver+9953-m3-10"
230               exclusive="true" component_name="m3-10.grenoble.iot-lab.info">
231             <hardware_type name="iotlab-node"/>
232             <location country="France"/>
233             <granularity grain="30"/>
234             <sliver_type name="iotlab-exclusive"/>
235         </node>
236         <lease slice_id="urn:publicid:IDN+onelab:inria+slice+test_iotlab"
237                start_time="1427792428" duration="29">
238             <node component_id=
239                 "urn:publicid:IDN+iotlab+node+m3-10.grenoble.iot-lab.info"/>
240         </lease>
241         ...
242         </rspec>
243
244         """
245         # pylint:disable=R0914,W0212
246         logger.warning("iotlabaggregate describe")
247         logger.warning("iotlabaggregate describe urns : %s" % urns)
248         if not options:
249             options = {}
250         version_manager = VersionManager()
251         version = version_manager.get_version(version)
252         rspec_version = version_manager._get_version(version.type,
253                                                      version.version,
254                                                      'manifest')
255         rspec = RSpec(version=rspec_version, user_options=options)
256         xrn = Xrn(urns[0])
257         geni_slivers = []
258
259         nodes = self.driver.shell.get_nodes()
260         reserved_nodes = self.driver.shell.get_reserved_nodes()
261         if not 'error' in nodes and not 'error' in reserved_nodes:
262             # find OAR jobs id for one slice in SFA database
263             db_leases = [(lease.job_id, lease.slice_hrn)
264                          for lease in self.driver.api.dbsession()
265                          .query(LeaseTable)
266                          .filter(LeaseTable.slice_hrn == xrn.hrn).all()]
267
268             leases = []
269             for job_id, slice_hrn in db_leases:
270                 # OAR job terminated, we delete entry in database
271                 if not job_id in reserved_nodes:
272                     self._delete_db_lease(job_id)
273                 else:
274                     # onelab slice = job submission from OneLAB
275                     lease = reserved_nodes[job_id]
276                     lease['slice_id'] = hrn_to_urn(slice_hrn, 'slice')
277                     leases.append(lease)
278
279             # get slivers
280             slivers = self.get_slivers(urns, leases, nodes)
281             if slivers:
282                 date = utcparse(slivers[0]['expires'])
283                 rspec_expires = datetime_to_string(date)
284             else:
285                 rspec_expires = datetime_to_string(utcparse(time.time()))
286             rspec.xml.set('expires', rspec_expires)
287
288             rspec_nodes = []
289
290             for sliver in slivers:
291                 rspec_node = self.sliver_to_rspec_node(sliver)
292                 rspec_nodes.append(rspec_node)
293                 geni_sliver = self.rspec_node_to_geni_sliver(rspec_node)
294                 geni_slivers.append(geni_sliver)
295             logger.warning("iotlabaggregate describe geni_slivers %s" %
296                            geni_slivers)
297             rspec.version.add_nodes(rspec_nodes)
298
299             rspec_leases = self.leases_to_rspec_leases(leases)
300             logger.warning("iotlabaggregate describe rspec_leases %s" %
301                            rspec_leases)
302             rspec.version.add_leases(rspec_leases)
303
304         return {'geni_urn': urns[0],
305                 'geni_rspec': rspec.toxml(),
306                 'geni_slivers': geni_slivers}
307