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