blob: 5978e9208987114ab3f70b75ea171405a670ab36 [file] [log] [blame]
Jay Pipes051075a2012-04-28 17:39:37 -04001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2012 OpenStack, LLC
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import logging
19import os
20import sys
21import shlex
22import subprocess
23
24import nose
25from sqlalchemy import create_engine, MetaData
26
27from tempest.common.utils.data_utils import rand_name
28from tempest.common.ssh import Client
29from tempest import exceptions
30from tempest import test
31from tempest.tests import compute
32
33LOG = logging.getLogger(__name__)
34
35
36class WhiteboxTest(object):
37
38 """
39 Base test case class mixin for "whitebox tests"
40
41 Whitebox tests are tests that have the following characteristics:
42
43 * Test common and advanced operations against a set of servers
44 * Use a client that it is possible to send random or bad data with
45 * SSH into either a host or a guest in order to validate server state
46 * May execute SQL queries directly against internal databases to verify
47 the state of data records
48 """
49 pass
50
51
52class ComputeWhiteboxTest(test.ComputeFuzzClientTest, WhiteboxTest):
53
54 """
55 Base smoke test case class for OpenStack Compute API (Nova)
56 """
57
58 @classmethod
59 def setUpClass(cls):
60 if not compute.WHITEBOX_ENABLED:
61 msg = "Whitebox testing disabled"
62 raise nose.SkipTest(msg)
63
64 super(ComputeWhiteboxTest, cls).setUpClass()
65
66 # Add some convenience attributes that tests use...
67 cls.nova_dir = cls.config.compute.source_dir
68 cls.compute_bin_dir = cls.config.compute.bin_dir
69 cls.compute_config_path = cls.config.compute.config_path
70 cls.servers_client = cls.manager.servers_client
71 cls.images_client = cls.manager.images_client
72 cls.flavors_client = cls.manager.flavors_client
73 cls.extensions_client = cls.manager.extensions_client
74 cls.floating_ips_client = cls.manager.floating_ips_client
75 cls.keypairs_client = cls.manager.keypairs_client
76 cls.security_groups_client = cls.manager.security_groups_client
77 cls.console_outputs_client = cls.manager.console_outputs_client
78 cls.limits_client = cls.manager.limits_client
79 cls.volumes_client = cls.manager.volumes_client
80 cls.build_interval = cls.config.compute.build_interval
81 cls.build_timeout = cls.config.compute.build_timeout
82 cls.ssh_user = cls.config.compute.ssh_user
83 cls.image_ref = cls.config.compute.image_ref
84 cls.image_ref_alt = cls.config.compute.image_ref_alt
85 cls.flavor_ref = cls.config.compute.flavor_ref
86 cls.flavor_ref_alt = cls.config.compute.flavor_ref_alt
87 cls.servers = []
88
89 @classmethod
90 def tearDownClass(cls):
91 # NOTE(jaypipes): Tests often add things in a particular order
92 # so we destroy resources in the reverse order in which resources
93 # are added to the test class object
94 if not cls.resources:
95 return
96 thing = cls.resources.pop()
97 while True:
98 LOG.debug("Deleting %r from shared resources of %s" %
99 (thing, cls.__name__))
100 # Resources in novaclient all have a delete() method
101 # which destroys the resource...
102 thing.delete()
103 if not cls.resources:
104 return
105 thing = cls.resources.pop()
106
107 @classmethod
108 def create_server(cls, image_id=None):
109 """Wrapper utility that returns a test server"""
110 server_name = rand_name(cls.__name__ + "-instance")
111 flavor = cls.flavor_ref
112 if not image_id:
113 image_id = cls.image_ref
114
115 resp, server = cls.servers_client.create_server(
116 server_name, image_id, flavor)
117 cls.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
118 cls.servers.append(server)
119 return server
120
121 @classmethod
122 def get_db_handle_and_meta(cls, database='nova'):
123 """Return a connection handle and metadata of an OpenStack database"""
124 engine_args = {"echo": False,
125 "convert_unicode": True,
126 "pool_recycle": 3600
127 }
128
129 try:
130 engine = create_engine(cls.config.compute.db_uri, **engine_args)
131 connection = engine.connect()
132 meta = MetaData()
133 meta.reflect(bind=engine)
134
135 except Exception, e:
136 raise exceptions.SQLException(message=e)
137
138 return connection, meta
139
140 def nova_manage(self, category, action, params):
141 """Executes nova-manage command for the given action"""
142
143 nova_manage_path = os.path.join(self.compute_bin_dir, 'nova-manage')
144 cmd = ' '.join([nova_manage_path, category, action, params])
145
146 if self.deploy_mode == 'devstack-local':
147 if not os.path.isdir(self.nova_dir):
148 sys.exit("Cannot find Nova source directory: %s" %
149 self.nova_dir)
150
151 cmd = shlex.split(cmd)
152 result = subprocess.Popen(cmd, stdout=subprocess.PIPE)
153
154 #Todo(rohitk): Need to define host connection parameters in config
155 else:
156 client = self.get_ssh_connection(self.config.whitebox.api_host,
157 self.config.whitebox.api_user,
158 self.config.whitebox.api_passwd)
159 result = client.exec_command(cmd)
160
161 return result
162
163 def get_ssh_connection(self, host, username, password):
164 """Create an SSH connection object to a host"""
165 ssh_timeout = self.config.compute.ssh_timeout
166 ssh_client = Client(host, username, password, ssh_timeout)
167 if not ssh_client.test_connection_auth():
168 raise exceptions.SSHTimeout()
169 else:
170 return ssh_client