56a4c45d78d19af090826f942fd59082e82d84e8
[sfa.git] / sfa / iotlab / iotlabdriver.py
1 # -*- coding:utf-8 -*-
2 """ driver class management """
3
4 from sfa.util.sfalogging import logger
5 from sfa.util.xrn import Xrn, urn_to_hrn
6 from sfa.rspecs.version_manager import VersionManager
7 from sfa.rspecs.rspec import RSpec
8 from sfa.managers.driver import Driver
9 from sfa.iotlab.iotlabshell import IotLABShell
10 from sfa.iotlab.iotlabaggregate import IotLABAggregate
11 from sfa.iotlab.iotlablease import LeaseTable
12
13 class IotLabDriver(Driver):
14     """
15     SFA driver for Iot-LAB testbed
16     """
17
18     def __init__(self, api):
19         Driver.__init__(self, api)
20         config = api.config
21         self.api = api
22         self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
23         self.shell = IotLABShell()
24         # need by sfa driver
25         self.cache = None
26
27     ########################################
28     ########## registry oriented
29     ########################################
30
31     ##########
32     def register(self, sfa_record, hrn, pub_key):
33         logger.warning("iotlabdriver register : not implemented")
34         return -1
35
36     ##########
37     def update(self, old_sfa_record, new_sfa_record, hrn, new_key):
38         logger.warning("iotlabdriver update : not implemented")
39         return True
40
41
42     ##########
43     def remove(self, sfa_record):
44         logger.warning("iotlabdriver remove : not implemented")
45         return True
46
47
48     ########################################
49     ########## aggregate oriented
50     ########################################
51
52     def provision(self, urns, options=None):
53         logger.warning("iotlabdriver provision : not implemented")
54         version_manager = VersionManager()
55         opt = options['geni_rspec_version']
56         rspec_version = version_manager.get_version(opt)
57         return self.describe(urns, rspec_version, options=options)
58
59
60     def delete(self, urns, options=None):
61         logger.warning("iotlabdriver delete : not implemented")
62         geni_slivers = []
63         return geni_slivers
64
65
66     def aggregate_version(self):
67         logger.warning("iotlabdriver aggregate_version")
68         version_manager = VersionManager()
69         ad_rspec_versions = []
70         request_rspec_versions = []
71         for rspec_version in version_manager.versions:
72             if rspec_version.content_type in ['*', 'ad']:
73                 ad_rspec_versions.append(rspec_version.to_dict())
74             if rspec_version.content_type in ['*', 'request']:
75                 request_rspec_versions.append(rspec_version.to_dict())
76         return {
77             'testbed': self.hrn,
78             'geni_request_rspec_versions': request_rspec_versions,
79             'geni_ad_rspec_versions': ad_rspec_versions}
80
81
82     def list_resources(self, version=None, options=None):
83         logger.warning("iotlabdriver list_resources")
84         if not options:
85             options = {}
86         aggregate = IotLABAggregate(self)
87         rspec = aggregate.list_resources(version=version, options=options)
88         return rspec
89
90
91     def describe(self, urns, version, options=None):
92         logger.warning("iotlabdriver describe")
93         if not options:
94             options = {}
95         aggregate = IotLABAggregate(self)
96         return aggregate.describe(urns, version=version, options=options)
97
98
99     def status(self, urns, options=None):
100         logger.warning("iotlabdriver status")
101         aggregate = IotLABAggregate(self)
102         desc = aggregate.describe(urns, version='GENI 3')
103         status = {'geni_urn': desc['geni_urn'],
104                   'geni_slivers': desc['geni_slivers']}
105         return status
106
107
108     def _get_users(self):
109         """ Get all users """
110         ret = self.shell.get_users()
111         if 'error' in ret:
112             return None
113         return ret
114
115
116     def _get_user_login(self, caller_user):
117         """ Get user login with email """
118         email = caller_user['email']
119         # ensure user exist in LDAP tree
120         users = self._get_users()
121         if users and not email in users:
122             self.shell.add_user(caller_user)
123             users = self._get_users()
124         if users and email in users:
125             return users[email]['login']
126         else:
127             return None
128
129
130     @classmethod
131     def _get_experiment(cls, rspec):
132         """
133         Find in RSpec leases the experiment start time, duration and nodes list.
134
135         :Example:
136         <rspec>
137         ...
138         <lease slice_id="urn:publicid:IDN+onelab:inria+slice+test_iotlab"
139                 start_time="1427792400" duration="30">
140             <node component_id=
141                 "urn:publicid:IDN+iotlab+node+m3-10.grenoble.iot-lab.info"/>
142         </lease>
143         <lease slice_id="urn:publicid:IDN+onelab:inria+slice+test_iotlab"
144                 start_time="1427792600" duration="50">
145             <node component_id=
146                 "urn:publicid:IDN+iotlab+node+m3-15.grenoble.iot-lab.info"/>
147         </lease>
148         ...
149         </rspec>
150         """
151         leases = rspec.version.get_leases()
152         start_time = min([int(lease['start_time'])
153                          for lease in leases])
154         end_time = max([int(lease['start_time']) +
155                        int(lease['duration'])
156                        for lease in leases])
157         nodes_list = [Xrn.unescape(Xrn(lease['component_id'].strip(),
158                       type='node').get_leaf())
159                       for lease in leases]
160         # uniq hostnames
161         nodes_list = list(set(nodes_list))
162         from math import floor
163         duration = floor((end_time - start_time)/60) # minutes
164         return nodes_list, start_time, duration
165
166
167     def _save_db_lease(self, job_id, slice_hrn):
168         """ Save lease table row in SFA database """
169         lease_row = LeaseTable(job_id,
170                                slice_hrn)
171         logger.warning("iotlabdriver _save_db_lease lease row : %s" %
172                        lease_row)
173         self.api.dbsession().add(lease_row)
174         self.api.dbsession().commit()
175
176
177     def allocate(self, urn, rspec_string, expiration, options=None):
178         """
179         Allocate method submit an experiment on Iot-LAB testbed with :
180             * user : get the slice user which launch request (caller_hrn)
181             * reservation : get the start time and duration in RSpec leases
182             * nodes : get the nodes list in RSpec leases
183         If we have a request success on Iot-LAB testbed we store in SFA
184         database the assocation OAR scheduler job id and slice hrn
185
186         :param urn : slice urn
187         :param rspec_string : RSpec received
188         :param options : options with slice users (geni_users)
189         """
190         # pylint:disable=R0914
191
192         logger.warning("iotlabdriver allocate")
193         xrn = Xrn(urn)
194         aggregate = IotLABAggregate(self)
195         # parse rspec
196         rspec = RSpec(rspec_string)
197
198         caller_hrn = options.get('actual_caller_hrn', [])
199         geni_users = options.get('geni_users', [])
200         caller_user = [user for user in geni_users if
201                        urn_to_hrn(user['urn'])[0] == caller_hrn][0]
202         logger.warning("iotlabdriver allocate caller : %s" %
203                        caller_user['email'])
204
205         login = self._get_user_login(caller_user)
206         # only if we have a user
207         if login:
208             nodes_list, start_time, duration = \
209                 self._get_experiment(rspec)
210             logger.warning("iotlabdriver allocate submit OAR job :"
211                            " %s %s %s %s" %
212                            (xrn.hrn, start_time, duration, nodes_list))
213             # [0-9A-Za-z_] with onelab.inria.test_iotlab
214             exp_name = '_'.join((xrn.hrn).split('.'))
215             # submit OAR job
216             ret = self.shell.reserve_nodes(login,
217                                            exp_name,
218                                            nodes_list,
219                                            start_time,
220                                            duration)
221
222             # in case of job submission success save slice and lease job
223             # id association in database
224             if 'id' in ret:
225                 self._save_db_lease(int(ret['id']),
226                                     xrn.hrn)
227
228         return aggregate.describe([xrn.get_urn()], version=rspec.version)