cosmetic
[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         logger.warning("iotlabdriver aggregate_version")
67         version_manager = VersionManager()
68         ad_rspec_versions = []
69         request_rspec_versions = []
70         for rspec_version in version_manager.versions:
71             if rspec_version.content_type in ['*', 'ad']:
72                 ad_rspec_versions.append(rspec_version.to_dict())
73             if rspec_version.content_type in ['*', 'request']:
74                 request_rspec_versions.append(rspec_version.to_dict())
75         return {
76             'testbed': self.hrn,
77             'geni_request_rspec_versions': request_rspec_versions,
78             'geni_ad_rspec_versions': ad_rspec_versions}
79
80     def list_resources(self, version=None, options=None):
81         logger.warning("iotlabdriver list_resources")
82         if not options:
83             options = {}
84         aggregate = IotLABAggregate(self)
85         rspec = aggregate.list_resources(version=version, options=options)
86         return rspec
87
88     def describe(self, urns, version, options=None):
89         logger.warning("iotlabdriver describe")
90         if not options:
91             options = {}
92         aggregate = IotLABAggregate(self)
93         return aggregate.describe(urns, version=version, options=options)
94
95     def status(self, urns, options=None):
96         logger.warning("iotlabdriver status")
97         aggregate = IotLABAggregate(self)
98         desc = aggregate.describe(urns, version='GENI 3')
99         status = {'geni_urn': desc['geni_urn'],
100                   'geni_slivers': desc['geni_slivers']}
101         return status
102
103     def _get_users(self):
104         """ Get all users """
105         ret = self.shell.get_users()
106         if 'error' in ret:
107             return None
108         return ret
109
110     def _get_user_login(self, caller_user):
111         """ Get user login with email """
112         email = caller_user['email']
113         # ensure user exist in LDAP tree
114         users = self._get_users()
115         if users and email not in users:
116             self.shell.add_user(caller_user)
117             users = self._get_users()
118         if users and email in users:
119             return users[email]['login']
120         else:
121             return None
122
123     @classmethod
124     def _get_experiment(cls, rspec):
125         """
126         Find in RSpec leases the experiment start time, duration and nodes
127         list.
128
129         :Example:
130         <rspec>
131         ...
132         <lease slice_id="urn:publicid:IDN+onelab:inria+slice+test_iotlab"
133                 start_time="1427792400" duration="30">
134             <node component_id=
135                 "urn:publicid:IDN+iotlab+node+m3-10.grenoble.iot-lab.info"/>
136         </lease>
137         <lease slice_id="urn:publicid:IDN+onelab:inria+slice+test_iotlab"
138                 start_time="1427792600" duration="50">
139             <node component_id=
140                 "urn:publicid:IDN+iotlab+node+m3-15.grenoble.iot-lab.info"/>
141         </lease>
142         ...
143         </rspec>
144         """
145         leases = rspec.version.get_leases()
146         start_time = min([int(lease['start_time'])
147                          for lease in leases])
148         end_time = max([int(lease['start_time']) +
149                        int(lease['duration'])*60
150                        for lease in leases])
151         nodes_list = [Xrn.unescape(Xrn(lease['component_id'].strip(),
152                       type='node').get_leaf())
153                       for lease in leases]
154         # uniq hostnames
155         nodes_list = list(set(nodes_list))
156         from math import floor
157         # minutes
158         duration = floor((end_time - start_time)/60)
159         return nodes_list, start_time, duration
160
161     def _save_db_lease(self, job_id, slice_hrn):
162         """ Save lease table row in SFA database """
163         lease_row = LeaseTable(job_id,
164                                slice_hrn)
165         logger.warning("iotlabdriver _save_db_lease lease row : %s" %
166                        lease_row)
167         self.api.dbsession().add(lease_row)
168         self.api.dbsession().commit()
169
170     def allocate(self, urn, rspec_string, expiration, options=None):
171         """
172         Allocate method submit an experiment on Iot-LAB testbed with :
173             * user : get the slice user which launch request (caller_hrn)
174             * reservation : get the start time and duration in RSpec leases
175             * nodes : get the nodes list in RSpec leases
176         If we have a request success on Iot-LAB testbed we store in SFA
177         database the assocation OAR scheduler job id and slice hrn
178
179         :param urn : slice urn
180         :param rspec_string : RSpec received
181         :param options : options with slice users (geni_users)
182         """
183         # pylint:disable=R0914
184
185         logger.warning("iotlabdriver allocate")
186         xrn = Xrn(urn)
187         aggregate = IotLABAggregate(self)
188         # parse rspec
189         rspec = RSpec(rspec_string)
190
191         caller_hrn = options.get('actual_caller_hrn', [])
192         geni_users = options.get('geni_users', [])
193         caller_user = [user for user in geni_users if
194                        urn_to_hrn(user['urn'])[0] == caller_hrn][0]
195         logger.warning("iotlabdriver allocate caller : %s" %
196                        caller_user['email'])
197
198         login = self._get_user_login(caller_user)
199         # only if we have a user
200         if login:
201             nodes_list, start_time, duration = \
202                 self._get_experiment(rspec)
203             logger.warning("iotlabdriver allocate submit OAR job :"
204                            " %s %s %s %s" %
205                            (xrn.hrn, start_time, duration, nodes_list))
206             # [0-9A-Za-z_] with onelab.inria.test_iotlab
207             exp_name = '_'.join((xrn.hrn).split('.'))
208             # submit OAR job
209             ret = self.shell.reserve_nodes(login,
210                                            exp_name,
211                                            nodes_list,
212                                            start_time,
213                                            duration)
214
215             # in case of job submission success save slice and lease job
216             # id association in database
217             if 'id' in ret:
218                 self._save_db_lease(int(ret['id']),
219                                     xrn.hrn)
220
221         return aggregate.describe([xrn.get_urn()], version=rspec.version)