Merge lp:~zulcss/uec-testing-scripts/uec-testing-scripts into lp:uec-testing-scripts
- uec-testing-scripts
- Merge into trunk
Proposed by
Chuck Short
Status: | Merged |
---|---|
Approved by: | C de-Avillez |
Approved revision: | 64 |
Merge reported by: | C de-Avillez |
Merged at revision: | not available |
Proposed branch: | lp:~zulcss/uec-testing-scripts/uec-testing-scripts |
Merge into: | lp:uec-testing-scripts |
Diff against target: |
510 lines (+461/-0) 5 files modified
openstack/config.yaml (+5/-0) openstack/uec_testing.py (+22/-0) openstack/uectesting/base.py (+116/-0) openstack/uectesting/testing.py (+312/-0) openstack/uectesting/utils.py (+6/-0) |
To merge this branch: | bzr merge lp:~zulcss/uec-testing-scripts/uec-testing-scripts |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
C de-Avillez (community) | Approve | ||
Review via email: mp+46058@code.launchpad.net |
Commit message
Description of the change
This branch breaks up openstack and uec.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'openstack' |
2 | === added directory 'openstack/bin' |
3 | === added file 'openstack/config.yaml' |
4 | --- openstack/config.yaml 1970-01-01 00:00:00 +0000 |
5 | +++ openstack/config.yaml 2011-01-13 00:51:02 +0000 |
6 | @@ -0,0 +1,5 @@ |
7 | +conf: |
8 | + kernel_image: ~/tmp/natty-server-uec-i386-vmlinuz-virtual |
9 | + kernel_name: natty-server-uec-i386-vmlinuz-virtual |
10 | + image: ~/tmp/natty-server-uec-i386.img |
11 | + image_name: natty-server-uec-i386.img |
12 | |
13 | === added file 'openstack/uec_testing.py' |
14 | --- openstack/uec_testing.py 1970-01-01 00:00:00 +0000 |
15 | +++ openstack/uec_testing.py 2011-01-13 00:51:02 +0000 |
16 | @@ -0,0 +1,22 @@ |
17 | +#!/usr/bin/python |
18 | + |
19 | +import optparse |
20 | +import yaml |
21 | +import sys |
22 | +import unittest |
23 | + |
24 | +from uectesting.utils import options |
25 | +from uectesting.testing import UECImageTest, UECInstanceTest, UECVolumeTest |
26 | + |
27 | +def main(): |
28 | + if options.config_file: |
29 | + suite = unittest.TestSuite() |
30 | + suite.addTest(unittest.makeSuite(UECImageTest)) |
31 | + suite.addTest(unittest.makeSuite(UECInstanceTest)) |
32 | + suite.addTest(unittest.makeSuite(UECVolumeTest)) |
33 | + |
34 | + unittest.TextTestRunner(verbosity=2).run(suite) |
35 | + |
36 | + |
37 | +if __name__ == "__main__": |
38 | + main() |
39 | |
40 | === added directory 'openstack/uectesting' |
41 | === added file 'openstack/uectesting/__init__.py' |
42 | === added file 'openstack/uectesting/base.py' |
43 | --- openstack/uectesting/base.py 1970-01-01 00:00:00 +0000 |
44 | +++ openstack/uectesting/base.py 2011-01-13 00:51:02 +0000 |
45 | @@ -0,0 +1,116 @@ |
46 | +import boto |
47 | +import commands |
48 | +import httplib |
49 | +import os |
50 | +import paramiko |
51 | +import random |
52 | +import sys |
53 | +import unittest |
54 | +import yaml |
55 | + |
56 | +from boto.ec2.regioninfo import RegionInfo |
57 | +from uectesting.utils import options |
58 | + |
59 | +class UECTestCase(unittest.TestCase): |
60 | + def connect_ssh(self, ip, key_name): |
61 | + # TODO(devcamcar): set a more reasonable connection timeout time |
62 | + key = paramiko.RSAKey.from_private_key_file('/tmp/%s.pem' % key_name) |
63 | + client = paramiko.SSHClient() |
64 | + client.set_missing_host_key_policy(paramiko.WarningPolicy()) |
65 | + client.connect(ip, username='root', pkey=key) |
66 | + stdin, stdout, stderr = client.exec_command('uptime') |
67 | + print 'uptime: ', stdout.read() |
68 | + return client |
69 | + |
70 | + def can_ping(self, ip): |
71 | + """ Attempt to ping the specified IP, and give up after 1 second. """ |
72 | + |
73 | + # NOTE(devcamcar): ping timeout flag is different in OSX. |
74 | + if sys.platform == 'darwin': |
75 | + timeout_flag = 't' |
76 | + else: |
77 | + timeout_flag = 'w' |
78 | + |
79 | + status, output = commands.getstatusoutput('ping -c1 -%s1 %s' % |
80 | + (timeout_flag, ip)) |
81 | + return status == 0 |
82 | + |
83 | + def get_connection(self, **kwargs): |
84 | + """ |
85 | + Returns a boto ec2 connection for the current environment. |
86 | + """ |
87 | + access_key = os.getenv('EC2_ACCESS_KEY') |
88 | + secret_key = os.getenv('EC2_SECRET_KEY') |
89 | + clc_url = os.getenv('EC2_URL') |
90 | + |
91 | + if not access_key or not secret_key or not clc_url: |
92 | + raise Exception('Missing EC2 environment variables. Please source ' |
93 | + 'the appropriate novarc file before running this ' |
94 | + 'test.') |
95 | + |
96 | + parts = self.split_clc_url(clc_url) |
97 | + return boto.connect_ec2(aws_access_key_id=access_key, |
98 | + aws_secret_access_key=secret_key, |
99 | + is_secure=parts['is_secure'], |
100 | + region=RegionInfo(None, |
101 | + 'nova', |
102 | + parts['ip']), |
103 | + port=parts['port'], |
104 | + path='/services/Cloud', |
105 | + **kwargs) |
106 | + |
107 | + def split_clc_url(self, clc_url): |
108 | + """ |
109 | + Splits a cloud controller endpoint url. |
110 | + """ |
111 | + parts = httplib.urlsplit(clc_url) |
112 | + is_secure = parts.scheme == 'https' |
113 | + ip, port = parts.netloc.split(':') |
114 | + return {'ip': ip, 'port': int(port), 'is_secure': is_secure} |
115 | + |
116 | + def create_key_pair(self, conn, key_name): |
117 | + try: |
118 | + os.remove('/tmp/%s.pem' % key_name) |
119 | + except: |
120 | + pass |
121 | + key = conn.create_key_pair(key_name) |
122 | + key.save('/tmp/') |
123 | + return key |
124 | + |
125 | + def delete_key_pair(self, conn, key_name): |
126 | + conn.delete_key_pair(key_name) |
127 | + try: |
128 | + os.remove('/tmp/%s.pem' % key_name) |
129 | + except: |
130 | + pass |
131 | + |
132 | + def bundle_image(self, image, kernel=False): |
133 | + cmd = 'euca-bundle-image -i %s' % image |
134 | + if kernel: |
135 | + cmd += ' --kernel true' |
136 | + status, output = commands.getstatusoutput(cmd) |
137 | + if status != 0: |
138 | + print '%s -> \n %s' % (cmd, output) |
139 | + raise Exception(output) |
140 | + return True |
141 | + |
142 | + def upload_image(self, bucket_name, image): |
143 | + cmd = 'euca-upload-bundle -b %s -m /tmp/%s.manifest.xml' % (bucket_name, image) |
144 | + status, output = commands.getstatusoutput(cmd) |
145 | + if status != 0: |
146 | + print '%s -> \n %s' % (cmd, output) |
147 | + raise Exception(output) |
148 | + return True |
149 | + |
150 | + def delete_bundle_bucket(self, bucket_name): |
151 | + cmd = 'euca-delete-bundle --clear -b %s' % (bucket_name) |
152 | + status, output = commands.getstatusoutput(cmd) |
153 | + if status != 0: |
154 | + print '%s -> \n%s' % (cmd, output) |
155 | + raise Exception(output) |
156 | + return True |
157 | + |
158 | + def load_config(self): |
159 | + conf = open(options.config_file, "r") |
160 | + config = yaml.load(conf) |
161 | + return config |
162 | |
163 | === added file 'openstack/uectesting/testing.py' |
164 | --- openstack/uectesting/testing.py 1970-01-01 00:00:00 +0000 |
165 | +++ openstack/uectesting/testing.py 2011-01-13 00:51:02 +0000 |
166 | @@ -0,0 +1,312 @@ |
167 | +import commands |
168 | +import os |
169 | +import random |
170 | +import socket |
171 | +import sys |
172 | +import time |
173 | +import unittest |
174 | + |
175 | +from uectesting import base |
176 | + |
177 | +TEST_PREFIX = 'test%s' % int (random.random()*1000000) |
178 | +TEST_BUCKET = '%s_bucket' % TEST_PREFIX |
179 | +TEST_KEY = '%s_key' % TEST_PREFIX |
180 | +TEST_DATA = {} |
181 | + |
182 | +class UECTestingCase(base.UECTestCase): |
183 | + def setUp(self): |
184 | + self.config = self.load_config() |
185 | + self.conn = self.get_connection() |
186 | + |
187 | + self.image = self.config['conf']['image'] |
188 | + self.image_name = self.config['conf']['image_name'] |
189 | + |
190 | + self.kernel_image = self.config['conf']['kernel_image'] |
191 | + self.kernel_name = self.config['conf']['kernel_name'] |
192 | + |
193 | + self.ramdisk_image = self.config['conf']['ramdisk_image'] |
194 | + self.ramdisk_name = self.config['conf']['ramdisk_name'] |
195 | + |
196 | + self.bucket_name = self.config['conf']['bucket'] |
197 | + |
198 | + global TEST_DATA |
199 | + self.data = TEST_DATA |
200 | + |
201 | +class UECImageTest(UECTestingCase): |
202 | + def setUp(self): |
203 | + global TEST_DATA |
204 | + self.data = TEST_DATA |
205 | + |
206 | + def test_001_can_bundle_image(self): |
207 | + self.assertTrue(self.bundle_image(self.image)) |
208 | + |
209 | + def test_002_can_upload_image(self): |
210 | + self.assertTrue(self.upload_image(TEST_BUCKET, self.image_name)) |
211 | + |
212 | + def test_003_can_register_image(self): |
213 | + image_id = self.conn.register_image('%s/%s.manifest.xml' % |
214 | + (TEST_BUCKET, self.image_name)) |
215 | + self.assert_(image_id is not None) |
216 | + self.data['image_id'] = image_id |
217 | + |
218 | + def test_004_can_bundle_kernel(self): |
219 | + self.assertTrue(self.bundle_image(self.kernel_image, kernel=True)) |
220 | + |
221 | + def test_005_can_upload_kernel(self): |
222 | + self.assertTrue(self.upload_image(TEST_BUCKET, self.kernel_name)) |
223 | + |
224 | + def test_006_can_register_kernel(self): |
225 | + kernel_id = self.conn.register_image('%s/%s.manifest.xml' % |
226 | + (TEST_BUCKET, self.kernel_name)) |
227 | + self.assert_(kernel_id is not None) |
228 | + self.data['kernel_id'] = kernel_id |
229 | + |
230 | + def test_007_images_are_available_within_10_seconds(self): |
231 | + for i in xrange(10): |
232 | + image = self.conn.get_image(self.data['image_id']) |
233 | + if image and image.state == 'available': |
234 | + break |
235 | + time.sleep(1) |
236 | + else: |
237 | + print image.state |
238 | + self.assert_(False) # wasn't available within 10 seconds |
239 | + self.assert_(image.type == 'machine') |
240 | + |
241 | + for i in xrange(10): |
242 | + kernel = self.conn.get_image(self.data['kernel_id']) |
243 | + if kernel and kernel.state == 'available': |
244 | + break |
245 | + time.sleep(1) |
246 | + else: |
247 | + self.assert_(False) # wasn't available within 10 seconds |
248 | + self.assert_(kernel.type == 'kernel') |
249 | + |
250 | + def test_008_can_describe_image_attribute(self): |
251 | + attrs = self.conn.get_image_attribute(self.data['image_id'], |
252 | + 'launchPermission') |
253 | + self.assert_(attrs.name, 'launch_permission') |
254 | + |
255 | + def test_009_can_modify_image_launch_permission(self): |
256 | + self.conn.modify_image_attribute(image_id=self.data['image_id'], |
257 | + operation='add', |
258 | + attribute='launchPermission', |
259 | + groups='all') |
260 | + image = self.conn.get_image(self.data['image_id']) |
261 | + self.assertEqual(image.id, self.data['image_id']) |
262 | + |
263 | + def test_010_can_see_launch_permission(self): |
264 | + attrs = self.conn.get_image_attribute(self.data['image_id'], |
265 | + 'launchPermission') |
266 | + self.assert_(attrs.name, 'launch_permission') |
267 | + self.assert_(attrs.attrs['groups'][0], 'all') |
268 | + |
269 | + def test_011_user_can_deregister_kernel(self): |
270 | + self.assertTrue(self.conn.deregister_image(self.data['kernel_id'])) |
271 | + |
272 | + def test_012_can_deregister_image(self): |
273 | + self.assertTrue(self.conn.deregister_image(self.data['image_id'])) |
274 | + |
275 | + def test_013_can_delete_bundle(self): |
276 | + self.assertTrue(self.delete_bundle_bucket(TEST_BUCKET)) |
277 | + |
278 | +class UECInstanceTest(UECTestingCase): |
279 | + def setUp(self): |
280 | + global TEST_DATA |
281 | + self.data = TEST_DATA |
282 | + |
283 | + def test_001_can_create_keypair(self): |
284 | + key = self.create_key_pair(self.conn, TEST_KEY) |
285 | + self.assertEqual(key.name, TEST_KEY) |
286 | + |
287 | + def test_002_can_create_instance_with_keypair(self): |
288 | + reservation = self.conn.run_instances(self.image_name, |
289 | + key_name=TEST_KEY, |
290 | + instance_type='m1.tiny') |
291 | + self.assertEqual(len(reservation.instances), 1) |
292 | + self.data['instance_id'] = reservation.instances[0].id |
293 | + |
294 | + def test_003_instance_runs_within_60_seconds(self): |
295 | + reservations = self.conn.get_all_instances([data['instance_id']]) |
296 | + instance = reservations[0].instances[0] |
297 | + # allow 60 seconds to exit pending with IP |
298 | + for x in xrange(60): |
299 | + instance.update() |
300 | + if instance.state == u'running': |
301 | + break |
302 | + time.sleep(1) |
303 | + else: |
304 | + self.fail('instance failed to start') |
305 | + ip = reservations[0].instances[0].private_dns_name |
306 | + self.failIf(ip == '0.0.0.0') |
307 | + self.data['private_ip'] = ip |
308 | + print self.data['private_ip'] |
309 | + |
310 | + def test_004_can_ping_private_ip(self): |
311 | + for x in xrange(120): |
312 | + # ping waits for 1 second |
313 | + status, output = commands.getstatusoutput( |
314 | + 'ping -c1 %s' % self.data['private_ip']) |
315 | + if status == 0: |
316 | + break |
317 | + else: |
318 | + self.fail('could not ping instance') |
319 | + |
320 | + def test_005_can_ssh_to_private_ip(self): |
321 | + for x in xrange(30): |
322 | + try: |
323 | + conn = self.connect_ssh(self.data['private_ip'], TEST_KEY) |
324 | + conn.close() |
325 | + except Exception: |
326 | + time.sleep(1) |
327 | + else: |
328 | + break |
329 | + else: |
330 | + self.fail('could not ssh to instance') |
331 | + |
332 | + def test_006_can_allocate_elastic_ip(self): |
333 | + result = self.conn.allocate_address() |
334 | + self.assertTrue(hasattr(result, 'public_ip')) |
335 | + self.data['public_ip'] = result.public_ip |
336 | + |
337 | + def test_007_can_associate_ip_with_instance(self): |
338 | + result = self.conn.associate_address(self.data['instance_id'], |
339 | + self.data['public_ip']) |
340 | + self.assertTrue(result) |
341 | + |
342 | + def test_008_can_ssh_with_public_ip(self): |
343 | + for x in xrange(30): |
344 | + try: |
345 | + conn = self.connect_ssh(self.data['public_ip'], TEST_KEY) |
346 | + conn.close() |
347 | + except socket.error: |
348 | + time.sleep(1) |
349 | + else: |
350 | + break |
351 | + else: |
352 | + self.fail('could not ssh to instance') |
353 | + |
354 | + def test_009_can_disassociate_ip_from_instance(self): |
355 | + result = self.conn.disassociate_address(self.data['public_ip']) |
356 | + self.assertTrue(result) |
357 | + |
358 | + def test_010_can_deallocate_elastic_ip(self): |
359 | + result = self.conn.release_address(self.data['public_ip']) |
360 | + self.assertTrue(result) |
361 | + |
362 | + def test_999_tearDown(self): |
363 | + self.delete_key_pair(self.conn, TEST_KEY) |
364 | + if self.data.has_key('instance_id'): |
365 | + self.conn.terminate_instances([data['instance_id']]) |
366 | + |
367 | + |
368 | +class UECVolumeTest(UECTestingCase): |
369 | + def setUp(self): |
370 | + super(UECVolumeTest, self).setUp() |
371 | + self.device = '/dev/vdb' |
372 | + |
373 | + global TEST_DATA |
374 | + self.data = TEST_DATA |
375 | + |
376 | + def test_000_setUp(self): |
377 | + self.create_key_pair(self.conn, TEST_KEY) |
378 | + reservation = self.conn.run_instances(self.image_name, |
379 | + instance_type='m1.tiny', |
380 | + key_name=TEST_KEY) |
381 | + instance = reservation.instances[0] |
382 | + self.data['instance'] = instance |
383 | + for x in xrange(120): |
384 | + if self.can_ping(instance.private_dns_name): |
385 | + break |
386 | + else: |
387 | + self.fail('unable to start instance') |
388 | + |
389 | + def test_001_can_create_volume(self): |
390 | + volume = self.conn.create_volume(1, 'nova') |
391 | + self.assertEqual(volume.size, 1) |
392 | + self.data['volume'] = volume |
393 | + # Give network time to find volume. |
394 | + time.sleep(5) |
395 | + |
396 | + def test_002_can_attach_volume(self): |
397 | + volume = self.data['volume'] |
398 | + |
399 | + for x in xrange(10): |
400 | + if volume.status == u'available': |
401 | + break |
402 | + time.sleep(5) |
403 | + volume.update() |
404 | + else: |
405 | + self.fail('cannot attach volume with state %s' % volume.status) |
406 | + |
407 | + volume.attach(self.data['instance'].id, self.device) |
408 | + |
409 | + # Volumes seems to report "available" too soon. |
410 | + for x in xrange(10): |
411 | + if volume.status == u'in-use': |
412 | + break |
413 | + time.sleep(5) |
414 | + volume.update() |
415 | + |
416 | + self.assertEqual(volume.status, u'in-use') |
417 | + |
418 | + # Give instance time to recognize volume. |
419 | + time.sleep(5) |
420 | + |
421 | + def test_003_can_mount_volume(self): |
422 | + ip = self.data['instance'].private_dns_name |
423 | + conn = self.connect_ssh(ip, TEST_KEY) |
424 | + commands = [] |
425 | + commands.append('mkdir -p /mnt/vol') |
426 | + commands.append('mkfs.ext2 %s' % self.device) |
427 | + commands.append('mount %s /mnt/vol' % self.device) |
428 | + commands.append('echo success') |
429 | + stdin, stdout, stderr = conn.exec_command(' && '.join(commands)) |
430 | + out = stdout.read() |
431 | + conn.close() |
432 | + if not out.strip().endswith('success'): |
433 | + self.fail('Unable to mount: %s %s' % (out, stderr.read())) |
434 | + |
435 | + def test_004_can_write_to_volume(self): |
436 | + ip = self.data['instance'].private_dns_name |
437 | + conn = self.connect_ssh(ip, TEST_KEY) |
438 | + # FIXME(devcamcar): This doesn't fail if the volume hasn't been mounted |
439 | + stdin, stdout, stderr = conn.exec_command( |
440 | + 'echo hello > /mnt/vol/test.txt') |
441 | + err = stderr.read() |
442 | + conn.close() |
443 | + if len(err) > 0: |
444 | + self.fail('Unable to write to mount: %s' % (err)) |
445 | + |
446 | + def test_005_volume_is_correct_size(self): |
447 | + ip = self.data['instance'].private_dns_name |
448 | + conn = self.connect_ssh(ip, TEST_KEY) |
449 | + stdin, stdout, stderr = conn.exec_command( |
450 | + "df -h | grep %s | awk {'print $2'}" % self.device) |
451 | + out = stdout.read() |
452 | + conn.close() |
453 | + if not out.strip() == '1008M': |
454 | + self.fail('Volume is not the right size: %s %s' % |
455 | + (out, stderr.read())) |
456 | + |
457 | + def test_006_me_can_umount_volume(self): |
458 | + ip = self.data['instance'].private_dns_name |
459 | + conn = self.connect_ssh(ip, TEST_KEY) |
460 | + stdin, stdout, stderr = conn.exec_command('umount /mnt/vol') |
461 | + err = stderr.read() |
462 | + conn.close() |
463 | + if len(err) > 0: |
464 | + self.fail('Unable to unmount: %s' % (err)) |
465 | + |
466 | + def test_007_me_can_detach_volume(self): |
467 | + result = self.conn.detach_volume(volume_id=self.data['volume'].id) |
468 | + self.assertTrue(result) |
469 | + time.sleep(5) |
470 | + |
471 | + def test_008_me_can_delete_volume(self): |
472 | + result = self.conn.delete_volume(self.data['volume'].id) |
473 | + self.assertTrue(result) |
474 | + |
475 | + def test_999_tearDown(self): |
476 | + self.conn.terminate_instances([self.data['instance'].id]) |
477 | + self.conn.delete_key_pair(TEST_KEY) |
478 | + |
479 | |
480 | === added file 'openstack/uectesting/utils.py' |
481 | --- openstack/uectesting/utils.py 1970-01-01 00:00:00 +0000 |
482 | +++ openstack/uectesting/utils.py 2011-01-13 00:51:02 +0000 |
483 | @@ -0,0 +1,6 @@ |
484 | +import optparse |
485 | + |
486 | + |
487 | +optparser = optparse.OptionParser() |
488 | +optparser.add_option('-c', dest='config_file', help='Config file to use') |
489 | +(options, args) = optparser.parse_args() |
490 | |
491 | === added directory 'uec' |
492 | === renamed file 'README' => 'uec/README' |
493 | === renamed file 'apt-upgrade.sh' => 'uec/apt-upgrade.sh' |
494 | === renamed file 'config_multi.yaml' => 'uec/config_multi.yaml' |
495 | === renamed file 'config_single.yaml' => 'uec/config_single.yaml' |
496 | === renamed file 'get-logs-ebs.sh' => 'uec/get-logs-ebs.sh' |
497 | === renamed file 'get-logs.sh' => 'uec/get-logs.sh' |
498 | === renamed file 'helper.sh' => 'uec/helper.sh' |
499 | === renamed directory 'images' => 'uec/images' |
500 | === renamed file 'myutils.py' => 'uec/myutils.py' |
501 | === renamed file 'reboot.sh' => 'uec/reboot.sh' |
502 | === renamed directory 'results' => 'uec/results' |
503 | === renamed file 'scp-uec.sh' => 'uec/scp-uec.sh' |
504 | === renamed file 'set-ssh.sh' => 'uec/set-ssh.sh' |
505 | === renamed file 'uec_ebs.py' => 'uec/uec_ebs.py' |
506 | === renamed file 'uec_instances.py' => 'uec/uec_instances.py' |
507 | === renamed file 'uec_test.py' => 'uec/uec_test.py' |
508 | === renamed file 'uec_utils.py' => 'uec/uec_utils.py' |
509 | === renamed directory 'users' => 'uec/users' |
510 | === renamed file 'vol-max-alloc.sh' => 'uec/vol-max-alloc.sh' |
approved.