6599a2b7ea8b3ba006648df39e30a384ebfb6468
[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, ip=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     if ip:
67         ec.set(node, "ip", ip)
68
69     ec.set(node, "cleanHome", True)
70     ec.set(node, "cleanProcesses", True)
71     
72     return node
73
74 class PLNodeFactoryTestCase(unittest.TestCase):
75
76     def test_creation_phase(self):
77         self.assertEquals(PlanetlabNode._rtype, "PlanetlabNode")
78         self.assertEquals(len(PlanetlabNode._attributes), 32)
79
80 class PLNodeTestCase(unittest.TestCase):
81     """
82     This tests use inria_nepi slice, from the test instance of MyPLC
83     nepiplc.pl.sophia.inria.fr. This test can fail if the user running
84     the test does not have a user in this instance of MyPLC or is not
85     added to the inria_nepi slice.
86     """
87
88     def setUp(self):
89         self.ec = DummyEC()
90         self.username = 'inria_nepi'
91         self.pl_user = os.environ.get("PL_USER")
92         self.pl_password = os.environ.get("PL_PASS")
93         self.pl_url = "nepiplc.pl.sophia.inria.fr"
94         commonapi=PLCAPIFactory.get_api(self.pl_user, self.pl_password,
95             self.pl_url)
96         commonapi.add_slice_nodes(self.username, ['nepi2.pl.sophia.inria.fr'])
97         commonapi._reserved = set()
98         commonapi._blacklist = set()
99
100     @skipIfNotPLCredentials
101     def test_plapi(self):
102         """
103         Check that the api to discover and reserve resources is well
104         instanciated, and is an instance of PLCAPI. Check that using
105         the same credentials, the same object of the api is used.
106         """
107         node1 = create_node(self.ec, self.username, pl_user=self.pl_user,
108             pl_password=self.pl_password, pl_url=self.pl_url, 
109             architecture="x86_64")
110
111         plnode_rm1 = self.ec.get_resource(node1)
112         hostname = plnode_rm1.get("hostname")
113         self.assertIsNone(hostname)
114
115         self.assertIsNone(plnode_rm1._node_to_provision)
116
117         api1 = plnode_rm1.plapi
118         self.assertIsInstance(api1, PLCAPI)
119         self.assertEquals(len(api1.reserved()), 0)
120         self.assertEquals(len(api1.blacklisted()), 0)
121
122         node2 = create_node(self.ec, self.username, pl_user=self.pl_user,   
123             pl_password=self.pl_password, pl_url=self.pl_url, 
124             architecture="x86_64")
125         
126         plnode_rm2 = self.ec.get_resource(node2)
127         api2 = plnode_rm2.plapi
128         self.assertEquals(api1, api2)
129     
130         # Set hostname attribute in order for the shutdown method not to fail
131         plnode_rm1._set_hostname_attr(3)
132         plnode_rm2._set_hostname_attr(3)
133
134     def test_no_plcredentials(self):
135         """
136         Check that if no PL credentials are set, the PlanetlabNode skip the 
137         discover and provision for the node.
138         """
139         node = create_node(self.ec, self.username, hostname='nepi2.pl.sophia.inria.fr')
140         plnode_rm = self.ec.get_resource(node)
141         
142         plnode_rm.do_discover()
143         plnode_rm.do_provision()
144         
145         self.assertIsNone(plnode_rm._node_to_provision)
146
147     @skipIfNotPLCredentials
148     def test_discover_inslice(self):
149         """
150         This test uses the fact that the node nepi2.pl.sophia.inria.fr is already in 
151         the slice and match the constraints OS Fedora12 and arch x86_64. 
152         Check nepi2.pl.sophia.inria.fr is alive if the test fails.
153         The node_id of nepi2.pl.sophia.inria.fr is 3.
154         """
155         node = create_node(self.ec, self.username, pl_user=self.pl_user,
156             pl_password=self.pl_password, pl_url=self.pl_url, architecture="x86_64", 
157             operatingSystem="f12")
158
159         plnode_rm = self.ec.get_resource(node)
160        
161         hostname = plnode_rm.get("hostname")
162         self.assertIsNone(hostname)
163
164         plnode_rm.do_discover()
165         self.assertEquals(plnode_rm._node_to_provision, 3)
166
167        # Set hostname attribute in order for the shutdown method not to fail
168         plnode_rm._set_hostname_attr(plnode_rm._node_to_provision)        
169
170     @skipIfNotPLCredentials
171     def test_discover_not_inslice(self):
172         """
173         This test checks that if the node is not in the slice, anyway the
174         discover method picks one that match constraints outside from the
175         slice.
176         """
177         node = create_node(self.ec, self.username, pl_user=self.pl_user,
178             pl_password=self.pl_password, pl_url=self.pl_url, country="France",
179             operatingSystem="f12")
180
181         plnode_rm = self.ec.get_resource(node)
182         plnode_rm.do_discover()
183     
184         result = [4] 
185         self.assertIn(plnode_rm._node_to_provision, result)     
186         self.assertIsNot(plnode_rm.plapi.reserved(), set())
187
188         # Set hostname attribute in order for the shutdown method not to fail
189         plnode_rm._set_hostname_attr(plnode_rm._node_to_provision)        
190
191     @skipIfNotPLCredentials
192     def test_discover_hostname(self):
193         """
194         This test checks that if the user specify the hostname, only that node
195         is discovered.
196         """
197         node = create_node(self.ec, self.username, pl_user=self.pl_user,
198             pl_password=self.pl_password, pl_url=self.pl_url, 
199             hostname="nepi2.pl.sophia.inria.fr")
200
201         plnode_rm = self.ec.get_resource(node)
202         plnode_rm.do_discover()
203
204         self.assertEquals(plnode_rm._node_to_provision, 3)
205         self.assertEquals(plnode_rm.plapi.reserved(), set([3]))
206
207     @skipIfNotPLCredentials
208     def test_discover_ip(self):
209         """
210         This test checks that if the user specify the ip, only that node
211         is discovered.
212         """
213         node = create_node(self.ec, self.username, pl_user=self.pl_user,
214             pl_password=self.pl_password, pl_url=self.pl_url,
215             ip="138.96.116.32")
216
217         plnode_rm = self.ec.get_resource(node)
218         plnode_rm.do_discover()
219
220         self.assertEquals(plnode_rm._node_to_provision, 3)
221         self.assertEquals(plnode_rm.plapi.reserved(), set([3]))
222         self.assertEquals(plnode_rm.get("hostname"), "nepi2.pl.sophia.inria.fr")
223
224     @skipIfNotPLCredentials
225     def test_discover_with_ranges(self):
226         """
227         Checks that defining max or min attributes, the discover method works.
228         """
229         node = create_node(self.ec, self.username, pl_user=self.pl_user,
230             pl_password=self.pl_password, pl_url=self.pl_url, minCpu=50)
231
232         plnode_rm = self.ec.get_resource(node)
233         plnode_rm.do_discover()
234
235         result = [6]
236         self.assertIn(plnode_rm._node_to_provision, result)
237         self.assertIsNot(plnode_rm.plapi.reserved(), set())
238
239         # Set hostname attribute in order for the shutdown method not to fail
240         plnode_rm._set_hostname_attr(plnode_rm._node_to_provision)        
241
242     @skipIfNotPLCredentials        
243     def test_blacklist_nodes(self):
244         """
245         Test that if the node is malfunctioning it gets blacklisted, the node
246         nepi1.pl.sophia.inria.fr is used, if the test fails, check that the 
247         result of the plcapi query is actually empty.
248         """
249         node = create_node(self.ec, self.username, pl_user=self.pl_user,
250                 pl_password=self.pl_password, pl_url=self.pl_url, 
251                 hostname="nepi1.pl.sophia.inria.fr")
252
253         plnode_rm = self.ec.get_resource(node)
254         self.assertEquals(plnode_rm.plapi.blacklisted(), set())
255
256         # check that the node is actually malfunctioning
257         api = plnode_rm.plapi
258         filters = {'boot_state': 'boot', 'node_type': 'regular', 
259             'hostname': 'nepi1.pl.sophia.inria.fr', 'run_level': 'boot',
260             '>last_contact': int(time.time()) - 2*3600}
261         node_id = api.get_nodes(filters, fields=['node_id'])
262
263         if not node_id:
264             with self.assertRaises(RuntimeError):
265                 plnode_rm.do_discover()
266                 self.assertEquals(plnode_rm.plapi.blacklisted(), set([1]))
267
268     @skipIfNotPLCredentials
269     def test_provision_node_inslice(self):
270         """
271         Check provision of the node nepi2.pl.sophia.inria.fr.
272         """
273         node = create_node(self.ec, self.username, pl_user=self.pl_user,
274             pl_password=self.pl_password, pl_url=self.pl_url, 
275             architecture="x86_64", operatingSystem="f12")
276
277         plnode_rm = self.ec.get_resource(node)
278         self.assertEquals(len(plnode_rm.plapi.blacklisted()), 0)
279         self.assertEquals(len(plnode_rm.plapi.reserved()), 0)
280
281         plnode_rm.do_discover()
282         plnode_rm.do_provision()
283         ip = plnode_rm.get("ip")
284         self.assertEquals(ip, "138.96.116.32")
285         self.assertEquals(len(plnode_rm.plapi.reserved()), 1)
286
287     @skipIfNotPLCredentials
288     def test_provision_node_not_inslice(self):
289         """
290         Check provision of one of the nodes f12, nodes:
291         'nepi3.pl.sophia.inria.fr'
292         'nepi5.pl.sophia.inria.fr'
293         """
294         node = create_node(self.ec, self.username, pl_user=self.pl_user,
295             pl_password=self.pl_password, pl_url=self.pl_url, operatingSystem="f12",
296             city='Paris')
297
298         plnode_rm = self.ec.get_resource(node)
299         self.assertEquals(plnode_rm.plapi.blacklisted(), set())
300         self.assertEquals(plnode_rm.plapi.reserved(), set())
301
302         plnode_rm.do_discover()
303         plnode_rm.do_provision()
304         ip = plnode_rm.get("ip")       
305
306         result = ["138.96.116.33","138.96.116.35"] 
307         self.assertIn(ip, result)
308        
309     @skipIfNotPLCredentials
310     def test_provision_more_than_available(self):
311         """
312         Check that if the user wants to provision 3 nodes in Paris, he
313         gets RuntimeError, there are only 2 nodes with city=Paris.
314         """
315         node1 = create_node(self.ec, self.username, pl_user=self.pl_user,
316             pl_password=self.pl_password, pl_url=self.pl_url, 
317             city="Paris", operatingSystem="f12")
318
319         plnode_rm1 = self.ec.get_resource(node1)
320         plnode_rm1.do_discover()
321         plnode_rm1.do_provision()
322
323         node2 = create_node(self.ec, self.username, pl_user=self.pl_user,
324             pl_password=self.pl_password, pl_url=self.pl_url, 
325             city="Paris", operatingSystem="f12")
326
327         plnode_rm2 = self.ec.get_resource(node2)
328         plnode_rm2.do_discover()
329         plnode_rm2.do_provision()
330
331         node3 = create_node(self.ec, self.username, pl_user=self.pl_user,
332             pl_password=self.pl_password, pl_url=self.pl_url, 
333             city="Paris", operatingSystem="f12")
334
335         plnode_rm3 = self.ec.get_resource(node3)
336         with self.assertRaises(RuntimeError):
337             plnode_rm3.do_discover()
338             with self.assertRaises(RuntimeError):
339                 plnode_rm3.do_provision()
340         
341         host1 = plnode_rm1.get('hostname')
342
343         plnode_rm3._set_hostname_attr(host1)
344
345     @skipIfNotPLCredentials
346     def test_concurrence(self):
347         """
348         Test with the nodes being discover and provision at the same time.
349         The deploy should fail as the test before, there aren't 4 nodes of 
350         that carachteristics.
351         """
352         node1 = create_node(self.ec, self.username, pl_user=self.pl_user,
353             pl_password=self.pl_password, pl_url=self.pl_url,
354             architecture="x86_64", operatingSystem="f12")
355
356         node2 = create_node(self.ec, self.username, pl_user=self.pl_user,
357             pl_password=self.pl_password, pl_url=self.pl_url,
358             architecture="x86_64", operatingSystem="f12")
359
360         node3 = create_node(self.ec, self.username, pl_user=self.pl_user,
361             pl_password=self.pl_password, pl_url=self.pl_url, 
362             architecture="x86_64", operatingSystem="f12")
363
364         node4 = create_node(self.ec, self.username, pl_user=self.pl_user,
365             pl_password=self.pl_password, pl_url=self.pl_url, 
366             architecture="x86_64", operatingSystem="f12")
367
368         self.ec.deploy()
369         self.ec.wait_deployed([node1, node2, node3, node4])
370         failure_level = self.ec._fm._failure_level
371         self.assertEqual(failure_level, 2)
372
373     def tearDown(self):
374         self.ec.shutdown()
375
376
377 if __name__ == '__main__':
378     unittest.main()
379
380
381