merging the nepi-3.0-pre-release branch with nepi-3-dev
[nepi.git] / test / resources / planetlab / node.py
1 #!/usr/bin/env python
2 #
3 #    NEPI, a framework to manage network experiments
4 #    Copyright (C) 2013 INRIA
5 #
6 #    This program is free software: you can redistribute it and/or modify
7 #    it under the terms of the GNU General Public License as published by
8 #    the Free Software Foundation, either version 3 of the License, or
9 #    (at your option) any later version.
10 #
11 #    This program is distributed in the hope that it will be useful,
12 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #    GNU General Public License for more details.
15 #
16 #    You should have received a copy of the GNU General Public License
17 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 #
19 # Author: Lucia Guevgeozian <lucia.guevgeozian_odizzio@inria.fr>
20
21 from nepi.execution.ec import ExperimentController
22
23 from nepi.resources.planetlab.node import PlanetlabNode
24 from nepi.resources.planetlab.plcapi import PLCAPI, PLCAPIFactory
25
26 from test_utils import skipIfNotPLCredentials
27
28 import os
29 import time
30 import unittest
31 import multiprocessing
32
33 class DummyEC(ExperimentController):
34     pass
35
36 def create_node(ec, username, pl_user=None, pl_password=None, pl_url=None, 
37                 hostname=None, country=None, operatingSystem=None, 
38                 minBandwidth=None, minCpu=None,
39                 architecture=None, city=None):
40
41     node = ec.register_resource("PlanetlabNode")
42
43     if username:
44         ec.set(node, "username", username)
45     if pl_user:
46         ec.set(node, "pluser", pl_user)
47     if pl_password:
48         ec.set(node, "plpassword", pl_password)
49     if pl_url:
50         ec.set(node, "plcApiUrl", pl_url)
51     
52     if hostname:
53         ec.set(node, "hostname", hostname)
54     if country:
55         ec.set(node, "country", country)
56     if operatingSystem:
57         ec.set(node, "operatingSystem", operatingSystem)
58     if minBandwidth:
59         iec.set(node, "minBandwidth", minBandwidth)
60     if minCpu:
61         ec.set(node, "minCpu", minCpu)
62     if architecture:
63         ec.set(node, "architecture", architecture)
64     if city:
65         ec.set(node, "city", city)
66
67     ec.set(node, "cleanHome", True)
68     ec.set(node, "cleanProcesses", True)
69     
70     return node
71
72 class PLNodeFactoryTestCase(unittest.TestCase):
73
74     def test_creation_phase(self):
75         self.assertEquals(PlanetlabNode._rtype, "PlanetlabNode")
76         self.assertEquals(len(PlanetlabNode._attributes), 30)
77
78 class PLNodeTestCase(unittest.TestCase):
79     """
80     This tests use inria_nepi slice, and  already added to
81     the slice, and ONLY this one in order for the test not to fail.
82     """
83
84     def setUp(self):
85         self.ec = DummyEC()
86         self.username = 'inria_nepi'
87         self.pl_user = os.environ.get("PL_USER")
88         self.pl_password = os.environ.get("PL_PASS")
89         self.pl_url = "nepiplc.pl.sophia.inria.fr"
90
91     @skipIfNotPLCredentials
92     def test_plapi(self):
93         """
94         Check that the api to discover and reserve resources is well
95         instanciated, and is an instance of PLCAPI. Ignore error while
96         executing the ec.shutdown method, the error is due to the name
97         of the host not being defined yet for this test.
98         """
99         node1 = create_node(self.ec, self.username, pl_user=self.pl_user,
100             pl_password=self.pl_password, pl_url=self.pl_url, 
101             architecture="x86_64")
102
103         plnode_rm1 = self.ec.get_resource(node1)
104         hostname = plnode_rm1.get("hostname")
105         self.assertIsNone(hostname)
106
107         self.assertIsNone(plnode_rm1._node_to_provision)
108
109         api1 = plnode_rm1.plapi
110         self.assertIsInstance(api1, PLCAPI)
111         self.assertEquals(len(api1.reserved()), 0)
112         self.assertEquals(len(api1.blacklisted()), 0)
113
114         node2 = create_node(self.ec, self.username, pl_user=self.pl_user,   
115             pl_password=self.pl_password, pl_url=self.pl_url, 
116             architecture="x86_64")
117         
118         plnode_rm2 = self.ec.get_resource(node2)
119         api2 = plnode_rm2.plapi
120         self.assertEquals(api1, api2)
121     
122         # Set hostname attribute in order for the shutdown method not to fail
123         plnode_rm1._set_hostname_attr(3)
124         plnode_rm2._set_hostname_attr(3)
125
126     def test_no_plcredentials(self):
127         """
128         Check that if no PL credentials are set, the PlanetlabNode skip the 
129         discover and provision for the node.
130         """
131         node = create_node(self.ec, self.username, hostname='nepi2.pl.sophia.inria.fr')
132         plnode_rm = self.ec.get_resource(node)
133         
134         plnode_rm.do_discover()
135         plnode_rm.do_provision()
136         
137         self.assertIsNone(plnode_rm._node_to_provision)
138
139     @skipIfNotPLCredentials
140     def test_discover_inslice(self):
141         """
142         This test uses the fact that the node nepi2.pl.sophia.inria.fr is already in 
143         the slice and match the constraints OS Fedora12 and arch x86_64.
144         Check nepi2.pl.sophia.inria.fr is alive if the test fails.
145         """
146         node = create_node(self.ec, self.username, pl_user=self.pl_user,
147             pl_password=self.pl_password, pl_url=self.pl_url, architecture="x86_64", 
148             operatingSystem="f12")
149
150         plnode_rm = self.ec.get_resource(node)
151        
152         hostname = plnode_rm.get("hostname")
153         self.assertIsNone(hostname)
154
155         api = plnode_rm.plapi
156         api.add_slice_nodes(self.username, ['nepi2.pl.sophia.inria.fr'])        
157
158         plnode_rm.do_discover()
159         self.assertEquals(plnode_rm._node_to_provision, 3)
160
161        # Set hostname attribute in order for the shutdown method not to fail
162         plnode_rm._set_hostname_attr(plnode_rm._node_to_provision)        
163
164     @skipIfNotPLCredentials
165     def test_discover_not_inslice(self):
166         """
167         This test checks that if the node is not in the slice, anyway the
168         discover method picks one that match constraints outside from the
169         slice.
170         """
171         node = create_node(self.ec, self.username, pl_user=self.pl_user,
172             pl_password=self.pl_password, pl_url=self.pl_url, country="France",
173             operatingSystem="f12")
174
175         plnode_rm = self.ec.get_resource(node)
176         plnode_rm.do_discover()
177     
178         result = [4] 
179         self.assertIn(plnode_rm._node_to_provision, result)     
180         self.assertIsNot(plnode_rm.plapi.reserved(), set())
181
182         # Set hostname attribute in order for the shutdown method not to fail
183         plnode_rm._set_hostname_attr(plnode_rm._node_to_provision)        
184
185     @skipIfNotPLCredentials
186     def test_discover_hostname(self):
187         """
188         This test checks that if the user specify the hostname, only that node
189         is discovered.
190         """
191         node = create_node(self.ec, self.username, pl_user=self.pl_user,
192             pl_password=self.pl_password, pl_url=self.pl_url, 
193             hostname="nepi2.pl.sophia.inria.fr")
194
195         plnode_rm = self.ec.get_resource(node)
196         plnode_rm.do_discover()
197
198         self.assertEquals(plnode_rm._node_to_provision, 3)
199         self.assertEquals(plnode_rm.plapi.reserved(), set([3]))
200
201     @skipIfNotPLCredentials
202     def test_discover_with_ranges(self):
203         """
204         Checks that defining max or min attributes, the discover method works.
205         """
206         node = create_node(self.ec, self.username, pl_user=self.pl_user,
207             pl_password=self.pl_password, pl_url=self.pl_url, minCpu=50)
208
209         plnode_rm = self.ec.get_resource(node)
210         plnode_rm.do_discover()
211
212         result = [6]
213         self.assertIn(plnode_rm._node_to_provision, result)
214         self.assertIsNot(plnode_rm.plapi.reserved(), set())
215
216         # Set hostname attribute in order for the shutdown method not to fail
217         plnode_rm._set_hostname_attr(plnode_rm._node_to_provision)        
218
219     @skipIfNotPLCredentials        
220     def test_blacklist_nodes(self):
221         """
222         Test that if the node is malfunctioning it gets blacklisted, the node
223         nepi1.pl.sophia.inria.fr is used, if the test fails, check that the 
224         result of the plcapi query is actually empty.
225         """
226         node = create_node(self.ec, self.username, pl_user=self.pl_user,
227                 pl_password=self.pl_password, pl_url=self.pl_url, 
228                 hostname="nepi1.pl.sophia.inria.fr")
229
230         plnode_rm = self.ec.get_resource(node)
231         self.assertEquals(plnode_rm.plapi.blacklisted(), set())
232
233         # check that the node is actually malfunctioning
234         api = plnode_rm.plapi
235         filters = {'boot_state': 'boot', 'node_type': 'regular', 
236             'hostname': 'nepi1.pl.sophia.inria.fr', 'run_level': 'boot',
237             '>last_contact': int(time.time()) - 2*3600}
238         node_id = api.get_nodes(filters, fields=['node_id'])
239
240         if not node_id:
241             with self.assertRaises(RuntimeError):
242                 plnode_rm.do_discover()
243                 self.assertEquals(plnode_rm.plapi.blacklisted(), set([1]))
244
245     @skipIfNotPLCredentials
246     def test_provision_node_inslice(self):
247         """
248         Check provision of the node nepi2.pl.sophia.inria.fr.
249         """
250         node = create_node(self.ec, self.username, pl_user=self.pl_user,
251             pl_password=self.pl_password, pl_url=self.pl_url, 
252             architecture="x86_64", operatingSystem="f12")
253
254         plnode_rm = self.ec.get_resource(node)
255         self.assertEquals(len(plnode_rm.plapi.blacklisted()), 0)
256         self.assertEquals(len(plnode_rm.plapi.reserved()), 0)
257
258         api = plnode_rm.plapi
259         api.add_slice_nodes(self.username, ['nepi2.pl.sophia.inria.fr'])
260
261         plnode_rm.do_discover()
262         plnode_rm.do_provision()
263         ip = plnode_rm.get("ip")
264         self.assertEquals(ip, "138.96.116.32")
265         self.assertEquals(len(plnode_rm.plapi.reserved()), 1)
266
267     @skipIfNotPLCredentials
268     def test_provision_node_not_inslice(self):
269         """
270         Check provision of one of the nodes f12, nodes:
271         'nepi3.pl.sophia.inria.fr'
272         'nepi5.pl.sophia.inria.fr'
273         """
274         node = create_node(self.ec, self.username, pl_user=self.pl_user,
275             pl_password=self.pl_password, pl_url=self.pl_url, operatingSystem="f12",
276             city='Paris')
277
278         plnode_rm = self.ec.get_resource(node)
279         self.assertEquals(plnode_rm.plapi.blacklisted(), set())
280         self.assertEquals(plnode_rm.plapi.reserved(), set())
281
282         api = plnode_rm.plapi
283         api.add_slice_nodes(self.username, ['nepi2.pl.sophia.inria.fr'])
284
285         plnode_rm.do_discover()
286         plnode_rm.do_provision()
287         ip = plnode_rm.get("ip")       
288
289         result = ["138.96.116.33","138.96.116.35"] 
290         self.assertIn(ip, result)
291        
292 #    @skipIfNotPLCredentials
293 #    def test_provision_more_than_available(self):
294 #        """
295 #        Check that if the user wants to provision 3 nodes in Paris, he
296 #        gets RuntimeError, there are only 2 nodes with city=Paris.
297 #        """
298 #        node1 = create_node(self.ec, self.username, pl_user=self.pl_user,
299 #            pl_password=self.pl_password, pl_url=self.pl_url, 
300 #            city="Paris", operatingSystem="f12")
301 #
302 #        plnode_rm1 = self.ec.get_resource(node1)
303 #
304 #        api = plnode_rm.plapi
305 #        api.add_slice_nodes(self.username, ['nepi2.pl.sophia.inria.fr'])
306 #
307 #        plnode_rm1.do_discover()
308 #        plnode_rm1.do_provision()
309 #
310 #        node2 = create_node(self.ec, self.username, pl_user=self.pl_user,
311 #            pl_password=self.pl_password, pl_url=self.pl_url, 
312 #            city="Paris", operatingSystem="f12")
313 #
314 #        plnode_rm2 = self.ec.get_resource(node2)
315 #        plnode_rm2.do_discover()
316 #        plnode_rm2.do_provision()
317 #
318 #        node3 = create_node(self.ec, self.username, pl_user=self.pl_user,
319 #            pl_password=self.pl_password, pl_url=self.pl_url, 
320 #            city="Paris", operatingSystem="f12")
321 #
322 #        plnode_rm3 = self.ec.get_resource(node3)
323 #        with self.assertRaises(RuntimeError):
324 #            plnode_rm3.do_discover()
325 #            with self.assertRaises(RuntimeError):
326 #                plnode_rm3.do_provision()
327 #        
328 #        host1 = plnode_rm1.get('hostname')
329 #
330 #        plnode_rm3._set_hostname_attr(host1)
331 #
332 #    @skipIfNotPLCredentials
333 #    def test_concurrence(self):
334 #        """
335 #        Test with the nodes being discover and provision at the same time.
336 #        It should fail as the test before.
337 #        """
338 #        node1 = create_node(self.ec, self.username, pl_user=self.pl_user,
339 #            pl_password=self.pl_password, pl_url=self.pl_url,
340 #            architecture="x86_64", operatingSystem="f12")
341 #
342 #        node2 = create_node(self.ec, self.username, pl_user=self.pl_user,
343 #            pl_password=self.pl_password, pl_url=self.pl_url,
344 #            architecture="x86_64", operatingSystem="f12")
345 #
346 #        node3 = create_node(self.ec, self.username, pl_user=self.pl_user,
347 #            pl_password=self.pl_password, pl_url=self.pl_url, 
348 #            architecture="x86_64", operatingSystem="f12")
349 #
350 #        node4 = create_node(self.ec, self.username, pl_user=self.pl_user,
351 #            pl_password=self.pl_password, pl_url=self.pl_url, 
352 #            architecture="x86_64", operatingSystem="f12")
353 #
354 #        self.ec.deploy()
355 #        self.ec.wait_finished([node1, node2, node3, node4])
356 #        state = self.ec.state(node1, hr=True)
357 #        self.assertIn(state, ['READY', 'FAILED'])
358 #        state = self.ec.state(node2, hr=True)
359 #        self.assertIn(state, ['READY', 'FAILED'])
360 #        state = self.ec.state(node3, hr=True)
361 #        self.assertIn(state, ['READY', 'FAILED'])
362 #        state = self.ec.state(node4, hr=True)
363 #        self.assertIn(state, ['READY', 'FAILED'])
364
365     def tearDown(self):
366         commonapi=PLCAPIFactory.get_api(self.pl_user, self.pl_password,
367             self.pl_url)
368         #commonapi.add_slice_nodes(self.username, ['nepi2.pl.sophia.inria.fr'])
369         commonapi._reserved = set()
370         commonapi._blacklist = set()
371              
372         self.ec.shutdown()
373
374
375 if __name__ == '__main__':
376     unittest.main()
377
378
379