Merge lp:~anso/nova/smoketests_fixes into lp:~hudson-openstack/nova/trunk

Proposed by Vish Ishaya
Status: Merged
Merged at revision: 835
Proposed branch: lp:~anso/nova/smoketests_fixes
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 584 lines (+334/-79)
8 files modified
nova/tests/api/openstack/test_servers.py (+0/-3)
run_tests.py (+2/-0)
smoketests/base.py (+16/-35)
smoketests/public_network_smoketests.py (+0/-6)
smoketests/run_tests.py (+297/-0)
smoketests/test_admin.py (+0/-7)
smoketests/test_netadmin.py (+3/-14)
smoketests/test_sysadmin.py (+16/-14)
To merge this branch: bzr merge lp:~anso/nova/smoketests_fixes
Reviewer Review Type Date Requested Status
Soren Hansen (community) Needs Information
Paul Voccio (community) Needs Fixing
Jay Pipes (community) Approve
Review via email: mp+51057@code.launchpad.net

Description of the change

One last fix to remove extra flag from admin_smoketests.

To post a comment you must log in.
Revision history for this message
Jay Pipes (jaypipes) wrote :

lgtm.

review: Approve
lp:~anso/nova/smoketests_fixes updated
531. By Vish Ishaya

more smoketest fixes

532. By Vish Ishaya

fix check for existing port 22 rule

533. By Vish Ishaya

merged trunk

534. By Vish Ishaya

merge clean db

535. By Vish Ishaya

make smoketests run with nose

536. By Vish Ishaya

add customizable tempdir and remove extra code

537. By Vish Ishaya

removed unused references to unittest

538. By Vish Ishaya

revert a few unnecessary changes to base.py

539. By Vish Ishaya

add timeout and retry for ssh

Revision history for this message
Soren Hansen (soren) wrote :

This needs a merge with trunk..

review: Needs Fixing
lp:~anso/nova/smoketests_fixes updated
540. By Vish Ishaya

merged trunk

Revision history for this message
Paul Voccio (pvo) wrote :

I got a merge conflict again...

review: Needs Fixing
Revision history for this message
Soren Hansen (soren) wrote :

I don't see how I'm supposed to pass flags to the tests anymore? I used to be able to just pass --test_image=this_or_that, I can't seem to get this past nose.

review: Needs Information
Revision history for this message
Soren Hansen (soren) wrote :

> I don't see how I'm supposed to pass flags to the tests anymore? I used to be
> able to just pass --test_image=this_or_that, I can't seem to get this past
> nose.

Ok, fixed in lp:~soren/nova/improve-smoketests which also includes this branch.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/tests/api/openstack/test_servers.py'
2--- nova/tests/api/openstack/test_servers.py 2011-03-02 17:39:30 +0000
3+++ nova/tests/api/openstack/test_servers.py 2011-03-08 20:42:48 +0000
4@@ -562,6 +562,3 @@
5
6 res = req.get_response(fakes.wsgi_app())
7 self.assertEqual(res.status_int, 400)
8-
9-if __name__ == "__main__":
10- unittest.main()
11
12=== modified file 'run_tests.py'
13--- run_tests.py 2011-02-25 01:04:25 +0000
14+++ run_tests.py 2011-03-08 20:42:48 +0000
15@@ -60,6 +60,8 @@
16 import unittest
17 import sys
18
19+gettext.install('nova', unicode=1)
20+
21 from nose import config
22 from nose import core
23 from nose import result
24
25=== modified file 'smoketests/base.py'
26--- smoketests/base.py 2011-02-23 02:04:08 +0000
27+++ smoketests/base.py 2011-03-08 20:42:48 +0000
28@@ -31,17 +31,24 @@
29 SUITE_NAMES = '[image, instance, volume]'
30 FLAGS = flags.FLAGS
31 flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES)
32+flags.DEFINE_integer('ssh_tries', 3, 'Numer of times to try ssh')
33 boto_v6 = None
34
35
36 class SmokeTestCase(unittest.TestCase):
37 def connect_ssh(self, ip, key_name):
38- # TODO(devcamcar): set a more reasonable connection timeout time
39 key = paramiko.RSAKey.from_private_key_file('/tmp/%s.pem' % key_name)
40- client = paramiko.SSHClient()
41- client.set_missing_host_key_policy(paramiko.WarningPolicy())
42- client.connect(ip, username='root', pkey=key)
43- return client
44+ tries = 0
45+ while(True):
46+ try:
47+ client = paramiko.SSHClient()
48+ client.set_missing_host_key_policy(paramiko.WarningPolicy())
49+ client.connect(ip, username='root', pkey=key, timeout=5)
50+ return client
51+ except (paramiko.AuthenticationException, paramiko.SSHException):
52+ tries += 1
53+ if tries == FLAGS.ssh_tries:
54+ raise
55
56 def can_ping(self, ip, command="ping"):
57 """Attempt to ping the specified IP, and give up after 1 second."""
58@@ -147,8 +154,8 @@
59 except:
60 pass
61
62- def bundle_image(self, image, kernel=False):
63- cmd = 'euca-bundle-image -i %s' % image
64+ def bundle_image(self, image, tempdir='/tmp', kernel=False):
65+ cmd = 'euca-bundle-image -i %s -d %s' % (image, tempdir)
66 if kernel:
67 cmd += ' --kernel true'
68 status, output = commands.getstatusoutput(cmd)
69@@ -157,9 +164,9 @@
70 raise Exception(output)
71 return True
72
73- def upload_image(self, bucket_name, image):
74+ def upload_image(self, bucket_name, image, tempdir='/tmp'):
75 cmd = 'euca-upload-bundle -b '
76- cmd += '%s -m /tmp/%s.manifest.xml' % (bucket_name, image)
77+ cmd += '%s -m %s/%s.manifest.xml' % (bucket_name, tempdir, image)
78 status, output = commands.getstatusoutput(cmd)
79 if status != 0:
80 print '%s -> \n %s' % (cmd, output)
81@@ -183,29 +190,3 @@
82 global TEST_DATA
83 self.conn = self.connection_for_env()
84 self.data = TEST_DATA
85-
86-
87-def run_tests(suites):
88- argv = FLAGS(sys.argv)
89- if FLAGS.use_ipv6:
90- global boto_v6
91- boto_v6 = __import__('boto_v6')
92-
93- if not os.getenv('EC2_ACCESS_KEY'):
94- print >> sys.stderr, 'Missing EC2 environment variables. Please ' \
95- 'source the appropriate novarc file before ' \
96- 'running this test.'
97- return 1
98-
99- if FLAGS.suite:
100- try:
101- suite = suites[FLAGS.suite]
102- except KeyError:
103- print >> sys.stderr, 'Available test suites:', \
104- ', '.join(suites.keys())
105- return 1
106-
107- unittest.TextTestRunner(verbosity=2).run(suite)
108- else:
109- for suite in suites.itervalues():
110- unittest.TextTestRunner(verbosity=2).run(suite)
111
112=== modified file 'smoketests/public_network_smoketests.py'
113--- smoketests/public_network_smoketests.py 2011-02-23 02:04:08 +0000
114+++ smoketests/public_network_smoketests.py 2011-03-08 20:42:48 +0000
115@@ -19,10 +19,8 @@
116 import commands
117 import os
118 import random
119-import socket
120 import sys
121 import time
122-import unittest
123
124 # If ../nova/__init__.py exists, add ../ to Python search path, so that
125 # it will override what happens to be installed in /usr/(local/)lib/python...
126@@ -181,7 +179,3 @@
127 self.conn.delete_security_group(security_group_name)
128 if 'instance_id' in self.data:
129 self.conn.terminate_instances([self.data['instance_id']])
130-
131-if __name__ == "__main__":
132- suites = {'instance': unittest.makeSuite(InstanceTestsFromPublic)}
133- sys.exit(base.run_tests(suites))
134
135=== added file 'smoketests/run_tests.py'
136--- smoketests/run_tests.py 1970-01-01 00:00:00 +0000
137+++ smoketests/run_tests.py 2011-03-08 20:42:48 +0000
138@@ -0,0 +1,297 @@
139+#!/usr/bin/env python
140+# vim: tabstop=4 shiftwidth=4 softtabstop=4
141+
142+# Copyright 2010 United States Government as represented by the
143+# Administrator of the National Aeronautics and Space Administration.
144+# All Rights Reserved.
145+#
146+# Licensed under the Apache License, Version 2.0 (the "License");
147+# you may not use this file except in compliance with the License.
148+# You may obtain a copy of the License at
149+#
150+# http://www.apache.org/licenses/LICENSE-2.0
151+#
152+# Unless required by applicable law or agreed to in writing, software
153+# distributed under the License is distributed on an "AS IS" BASIS,
154+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
155+# See the License for the specific language governing permissions and
156+# limitations under the License.
157+
158+# Colorizer Code is borrowed from Twisted:
159+# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
160+#
161+# Permission is hereby granted, free of charge, to any person obtaining
162+# a copy of this software and associated documentation files (the
163+# "Software"), to deal in the Software without restriction, including
164+# without limitation the rights to use, copy, modify, merge, publish,
165+# distribute, sublicense, and/or sell copies of the Software, and to
166+# permit persons to whom the Software is furnished to do so, subject to
167+# the following conditions:
168+#
169+# The above copyright notice and this permission notice shall be
170+# included in all copies or substantial portions of the Software.
171+#
172+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
173+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
174+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
175+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
176+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
177+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
178+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
179+"""Unittest runner for Nova.
180+
181+To run all tests
182+ python run_tests.py
183+
184+To run a single test:
185+ python run_tests.py test_compute:ComputeTestCase.test_run_terminate
186+
187+To run a single test module:
188+ python run_tests.py test_compute
189+
190+ or
191+
192+ python run_tests.py api.test_wsgi
193+
194+"""
195+
196+import gettext
197+import os
198+import unittest
199+import sys
200+
201+gettext.install('nova', unicode=1)
202+
203+from nose import config
204+from nose import core
205+from nose import result
206+
207+
208+class _AnsiColorizer(object):
209+ """
210+ A colorizer is an object that loosely wraps around a stream, allowing
211+ callers to write text to the stream in a particular color.
212+
213+ Colorizer classes must implement C{supported()} and C{write(text, color)}.
214+ """
215+ _colors = dict(black=30, red=31, green=32, yellow=33,
216+ blue=34, magenta=35, cyan=36, white=37)
217+
218+ def __init__(self, stream):
219+ self.stream = stream
220+
221+ def supported(cls, stream=sys.stdout):
222+ """
223+ A class method that returns True if the current platform supports
224+ coloring terminal output using this method. Returns False otherwise.
225+ """
226+ if not stream.isatty():
227+ return False # auto color only on TTYs
228+ try:
229+ import curses
230+ except ImportError:
231+ return False
232+ else:
233+ try:
234+ try:
235+ return curses.tigetnum("colors") > 2
236+ except curses.error:
237+ curses.setupterm()
238+ return curses.tigetnum("colors") > 2
239+ except:
240+ raise
241+ # guess false in case of error
242+ return False
243+ supported = classmethod(supported)
244+
245+ def write(self, text, color):
246+ """
247+ Write the given text to the stream in the given color.
248+
249+ @param text: Text to be written to the stream.
250+
251+ @param color: A string label for a color. e.g. 'red', 'white'.
252+ """
253+ color = self._colors[color]
254+ self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
255+
256+
257+class _Win32Colorizer(object):
258+ """
259+ See _AnsiColorizer docstring.
260+ """
261+ def __init__(self, stream):
262+ from win32console import GetStdHandle, STD_OUT_HANDLE, \
263+ FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
264+ FOREGROUND_INTENSITY
265+ red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
266+ FOREGROUND_BLUE, FOREGROUND_INTENSITY)
267+ self.stream = stream
268+ self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
269+ self._colors = {
270+ 'normal': red | green | blue,
271+ 'red': red | bold,
272+ 'green': green | bold,
273+ 'blue': blue | bold,
274+ 'yellow': red | green | bold,
275+ 'magenta': red | blue | bold,
276+ 'cyan': green | blue | bold,
277+ 'white': red | green | blue | bold
278+ }
279+
280+ def supported(cls, stream=sys.stdout):
281+ try:
282+ import win32console
283+ screenBuffer = win32console.GetStdHandle(
284+ win32console.STD_OUT_HANDLE)
285+ except ImportError:
286+ return False
287+ import pywintypes
288+ try:
289+ screenBuffer.SetConsoleTextAttribute(
290+ win32console.FOREGROUND_RED |
291+ win32console.FOREGROUND_GREEN |
292+ win32console.FOREGROUND_BLUE)
293+ except pywintypes.error:
294+ return False
295+ else:
296+ return True
297+ supported = classmethod(supported)
298+
299+ def write(self, text, color):
300+ color = self._colors[color]
301+ self.screenBuffer.SetConsoleTextAttribute(color)
302+ self.stream.write(text)
303+ self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
304+
305+
306+class _NullColorizer(object):
307+ """
308+ See _AnsiColorizer docstring.
309+ """
310+ def __init__(self, stream):
311+ self.stream = stream
312+
313+ def supported(cls, stream=sys.stdout):
314+ return True
315+ supported = classmethod(supported)
316+
317+ def write(self, text, color):
318+ self.stream.write(text)
319+
320+
321+class NovaTestResult(result.TextTestResult):
322+ def __init__(self, *args, **kw):
323+ result.TextTestResult.__init__(self, *args, **kw)
324+ self._last_case = None
325+ self.colorizer = None
326+ # NOTE(vish): reset stdout for the terminal check
327+ stdout = sys.stdout
328+ sys.stdout = sys.__stdout__
329+ for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
330+ if colorizer.supported():
331+ self.colorizer = colorizer(self.stream)
332+ break
333+ sys.stdout = stdout
334+
335+ def getDescription(self, test):
336+ return str(test)
337+
338+ # NOTE(vish): copied from unittest with edit to add color
339+ def addSuccess(self, test):
340+ unittest.TestResult.addSuccess(self, test)
341+ if self.showAll:
342+ self.colorizer.write("OK", 'green')
343+ self.stream.writeln()
344+ elif self.dots:
345+ self.stream.write('.')
346+ self.stream.flush()
347+
348+ # NOTE(vish): copied from unittest with edit to add color
349+ def addFailure(self, test, err):
350+ unittest.TestResult.addFailure(self, test, err)
351+ if self.showAll:
352+ self.colorizer.write("FAIL", 'red')
353+ self.stream.writeln()
354+ elif self.dots:
355+ self.stream.write('F')
356+ self.stream.flush()
357+
358+ # NOTE(vish): copied from nose with edit to add color
359+ def addError(self, test, err):
360+ """Overrides normal addError to add support for
361+ errorClasses. If the exception is a registered class, the
362+ error will be added to the list for that class, not errors.
363+ """
364+ stream = getattr(self, 'stream', None)
365+ ec, ev, tb = err
366+ try:
367+ exc_info = self._exc_info_to_string(err, test)
368+ except TypeError:
369+ # 2.3 compat
370+ exc_info = self._exc_info_to_string(err)
371+ for cls, (storage, label, isfail) in self.errorClasses.items():
372+ if result.isclass(ec) and issubclass(ec, cls):
373+ if isfail:
374+ test.passed = False
375+ storage.append((test, exc_info))
376+ # Might get patched into a streamless result
377+ if stream is not None:
378+ if self.showAll:
379+ message = [label]
380+ detail = result._exception_detail(err[1])
381+ if detail:
382+ message.append(detail)
383+ stream.writeln(": ".join(message))
384+ elif self.dots:
385+ stream.write(label[:1])
386+ return
387+ self.errors.append((test, exc_info))
388+ test.passed = False
389+ if stream is not None:
390+ if self.showAll:
391+ self.colorizer.write("ERROR", 'red')
392+ self.stream.writeln()
393+ elif self.dots:
394+ stream.write('E')
395+
396+ def startTest(self, test):
397+ unittest.TestResult.startTest(self, test)
398+ current_case = test.test.__class__.__name__
399+
400+ if self.showAll:
401+ if current_case != self._last_case:
402+ self.stream.writeln(current_case)
403+ self._last_case = current_case
404+
405+ self.stream.write(
406+ ' %s' % str(test.test._testMethodName).ljust(60))
407+ self.stream.flush()
408+
409+
410+class NovaTestRunner(core.TextTestRunner):
411+ def _makeResult(self):
412+ return NovaTestResult(self.stream,
413+ self.descriptions,
414+ self.verbosity,
415+ self.config)
416+
417+
418+if __name__ == '__main__':
419+ if not os.getenv('EC2_ACCESS_KEY'):
420+ print _('Missing EC2 environment variables. Please ' \
421+ 'source the appropriate novarc file before ' \
422+ 'running this test.')
423+ sys.exit(1)
424+
425+ testdir = os.path.abspath("./")
426+ c = config.Config(stream=sys.stdout,
427+ env=os.environ,
428+ verbosity=3,
429+ workingDir=testdir,
430+ plugins=core.DefaultPluginManager())
431+
432+ runner = NovaTestRunner(stream=c.stream,
433+ verbosity=c.verbosity,
434+ config=c)
435+ sys.exit(not core.run(config=c, testRunner=runner, argv=sys.argv))
436
437=== renamed file 'smoketests/admin_smoketests.py' => 'smoketests/test_admin.py'
438--- smoketests/admin_smoketests.py 2011-01-12 08:47:54 +0000
439+++ smoketests/test_admin.py 2011-03-08 20:42:48 +0000
440@@ -35,10 +35,7 @@
441 from smoketests import base
442
443
444-SUITE_NAMES = '[user]'
445-
446 FLAGS = flags.FLAGS
447-flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES)
448
449 # TODO(devamcar): Use random tempfile
450 ZIP_FILENAME = '/tmp/nova-me-x509.zip'
451@@ -92,7 +89,3 @@
452 os.remove(ZIP_FILENAME)
453 except:
454 pass
455-
456-if __name__ == "__main__":
457- suites = {'user': unittest.makeSuite(UserTests)}
458- sys.exit(base.run_tests(suites))
459
460=== renamed file 'smoketests/netadmin_smoketests.py' => 'smoketests/test_netadmin.py'
461--- smoketests/netadmin_smoketests.py 2011-02-23 02:04:32 +0000
462+++ smoketests/test_netadmin.py 2011-03-08 20:42:48 +0000
463@@ -21,7 +21,6 @@
464 import random
465 import sys
466 import time
467-import unittest
468
469 # If ../nova/__init__.py exists, add ../ to Python search path, so that
470 # it will override what happens to be installed in /usr/(local/)lib/python...
471@@ -74,8 +73,10 @@
472 groups = self.conn.get_all_security_groups(['default'])
473 for rule in groups[0].rules:
474 if (rule.ip_protocol == 'tcp' and
475- rule.from_port <= 22 and rule.to_port >= 22):
476+ int(rule.from_port) <= 22 and
477+ int(rule.to_port) >= 22):
478 ssh_authorized = True
479+ break
480 if not ssh_authorized:
481 self.conn.authorize_security_group('default',
482 ip_protocol='tcp',
483@@ -137,11 +138,6 @@
484 if not self.wait_for_running(self.data['instance']):
485 self.fail('instance failed to start')
486 self.data['instance'].update()
487- if not self.wait_for_ping(self.data['instance'].private_dns_name):
488- self.fail('could not ping instance')
489- if not self.wait_for_ssh(self.data['instance'].private_dns_name,
490- TEST_KEY):
491- self.fail('could not ssh to instance')
492
493 def test_003_can_authorize_security_group_ingress(self):
494 self.assertTrue(self.conn.authorize_security_group(TEST_GROUP,
495@@ -185,10 +181,3 @@
496 self.assertFalse(TEST_GROUP in [group.name for group in groups])
497 self.conn.terminate_instances([self.data['instance'].id])
498 self.assertTrue(self.conn.release_address(self.data['public_ip']))
499-
500-
501-if __name__ == "__main__":
502- suites = {'address': unittest.makeSuite(AddressTests),
503- 'security_group': unittest.makeSuite(SecurityGroupTests)
504- }
505- sys.exit(base.run_tests(suites))
506
507=== renamed file 'smoketests/sysadmin_smoketests.py' => 'smoketests/test_sysadmin.py'
508--- smoketests/sysadmin_smoketests.py 2011-02-23 02:04:08 +0000
509+++ smoketests/test_sysadmin.py 2011-03-08 20:42:48 +0000
510@@ -16,12 +16,12 @@
511 # License for the specific language governing permissions and limitations
512 # under the License.
513
514-import commands
515 import os
516 import random
517 import sys
518 import time
519-import unittest
520+import tempfile
521+import shutil
522
523 # If ../nova/__init__.py exists, add ../ to Python search path, so that
524 # it will override what happens to be installed in /usr/(local/)lib/python...
525@@ -48,10 +48,18 @@
526 TEST_GROUP = '%s_group' % TEST_PREFIX
527 class ImageTests(base.UserSmokeTestCase):
528 def test_001_can_bundle_image(self):
529- self.assertTrue(self.bundle_image(FLAGS.bundle_image))
530+ self.data['tempdir'] = tempfile.mkdtemp()
531+ self.assertTrue(self.bundle_image(FLAGS.bundle_image,
532+ self.data['tempdir']))
533
534 def test_002_can_upload_image(self):
535- self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_image))
536+ try:
537+ self.assertTrue(self.upload_image(TEST_BUCKET,
538+ FLAGS.bundle_image,
539+ self.data['tempdir']))
540+ finally:
541+ if os.path.exists(self.data['tempdir']):
542+ shutil.rmtree(self.data['tempdir'])
543
544 def test_003_can_register_image(self):
545 image_id = self.conn.register_image('%s/%s.manifest.xml' %
546@@ -191,7 +199,7 @@
547 self.assertEqual(volume.size, 1)
548 self.data['volume'] = volume
549 # Give network time to find volume.
550- time.sleep(10)
551+ time.sleep(5)
552
553 def test_002_can_attach_volume(self):
554 volume = self.data['volume']
555@@ -204,6 +212,8 @@
556 else:
557 self.fail('cannot attach volume with state %s' % volume.status)
558
559+ # Give volume some time to be ready.
560+ time.sleep(5)
561 volume.attach(self.data['instance'].id, self.device)
562
563 # wait
564@@ -218,7 +228,7 @@
565 self.assertTrue(volume.status.startswith('in-use'))
566
567 # Give instance time to recognize volume.
568- time.sleep(10)
569+ time.sleep(5)
570
571 def test_003_can_mount_volume(self):
572 ip = self.data['instance'].private_dns_name
573@@ -283,11 +293,3 @@
574 def test_999_tearDown(self):
575 self.conn.terminate_instances([self.data['instance'].id])
576 self.conn.delete_key_pair(TEST_KEY)
577-
578-
579-if __name__ == "__main__":
580- suites = {'image': unittest.makeSuite(ImageTests),
581- 'instance': unittest.makeSuite(InstanceTests),
582- 'volume': unittest.makeSuite(VolumeTests)
583- }
584- sys.exit(base.run_tests(suites))