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