Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 1 | # 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 | |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 18 | import os |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 19 | import shlex |
| 20 | import subprocess |
Matthew Treinish | a83a16e | 2012-12-07 13:44:02 -0500 | [diff] [blame] | 21 | import sys |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 22 | |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 23 | from sqlalchemy import create_engine, MetaData |
Matthew Treinish | f4a9b0f | 2013-07-26 16:58:26 -0400 | [diff] [blame] | 24 | |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 25 | from tempest.common.ssh import Client |
Matthew Treinish | a83a16e | 2012-12-07 13:44:02 -0500 | [diff] [blame] | 26 | from tempest.common.utils.data_utils import rand_name |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 27 | from tempest import exceptions |
Matthew Treinish | f4a9b0f | 2013-07-26 16:58:26 -0400 | [diff] [blame] | 28 | from tempest.openstack.common import log as logging |
Attila Fazekas | d6d8629 | 2013-07-02 16:15:01 +0200 | [diff] [blame] | 29 | from tempest.scenario import manager |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 30 | |
| 31 | LOG = logging.getLogger(__name__) |
| 32 | |
| 33 | |
| 34 | class WhiteboxTest(object): |
| 35 | |
| 36 | """ |
| 37 | Base test case class mixin for "whitebox tests" |
| 38 | |
| 39 | Whitebox tests are tests that have the following characteristics: |
| 40 | |
| 41 | * Test common and advanced operations against a set of servers |
| 42 | * Use a client that it is possible to send random or bad data with |
| 43 | * SSH into either a host or a guest in order to validate server state |
| 44 | * May execute SQL queries directly against internal databases to verify |
| 45 | the state of data records |
| 46 | """ |
| 47 | pass |
| 48 | |
| 49 | |
Attila Fazekas | d6d8629 | 2013-07-02 16:15:01 +0200 | [diff] [blame] | 50 | class ComputeWhiteboxTest(manager.OfficialClientTest): |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 51 | |
| 52 | """ |
| 53 | Base smoke test case class for OpenStack Compute API (Nova) |
| 54 | """ |
| 55 | |
| 56 | @classmethod |
| 57 | def setUpClass(cls): |
Sean Dague | 2203a1f | 2013-05-14 11:50:46 -0400 | [diff] [blame] | 58 | super(ComputeWhiteboxTest, cls).setUpClass() |
| 59 | if not cls.config.whitebox.whitebox_enabled: |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 60 | msg = "Whitebox testing disabled" |
ivan-zhu | 1feeb38 | 2013-01-24 10:14:39 +0800 | [diff] [blame] | 61 | raise cls.skipException(msg) |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 62 | |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 63 | # Add some convenience attributes that tests use... |
Attila Fazekas | 3ca1fb3 | 2013-01-21 23:10:53 +0100 | [diff] [blame] | 64 | cls.nova_dir = cls.config.whitebox.source_dir |
| 65 | cls.compute_bin_dir = cls.config.whitebox.bin_dir |
| 66 | cls.compute_config_path = cls.config.whitebox.config_path |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 67 | cls.build_interval = cls.config.compute.build_interval |
| 68 | cls.build_timeout = cls.config.compute.build_timeout |
| 69 | cls.ssh_user = cls.config.compute.ssh_user |
| 70 | cls.image_ref = cls.config.compute.image_ref |
| 71 | cls.image_ref_alt = cls.config.compute.image_ref_alt |
| 72 | cls.flavor_ref = cls.config.compute.flavor_ref |
| 73 | cls.flavor_ref_alt = cls.config.compute.flavor_ref_alt |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 74 | |
Attila Fazekas | c3a095b | 2013-08-17 09:15:44 +0200 | [diff] [blame] | 75 | # NOTE(afazekas): Mimics the helper method used in the api tests |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 76 | @classmethod |
Attila Fazekas | d6d8629 | 2013-07-02 16:15:01 +0200 | [diff] [blame] | 77 | def create_server(cls, **kwargs): |
| 78 | flavor_ref = cls.config.compute.flavor_ref |
| 79 | image_ref = cls.config.compute.image_ref |
| 80 | name = rand_name(cls.__name__ + "-instance") |
| 81 | if 'name' in kwargs: |
| 82 | name = kwargs.pop('name') |
| 83 | flavor = kwargs.get('flavor', flavor_ref) |
| 84 | image_id = kwargs.get('image_id', image_ref) |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 85 | |
Attila Fazekas | d6d8629 | 2013-07-02 16:15:01 +0200 | [diff] [blame] | 86 | server = cls.compute_client.servers.create( |
| 87 | name, image_id, flavor, **kwargs) |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 88 | |
Attila Fazekas | d6d8629 | 2013-07-02 16:15:01 +0200 | [diff] [blame] | 89 | if 'wait_until' in kwargs: |
| 90 | cls.status_timeout(cls.compute_client.servers, server.id, |
| 91 | server['id'], kwargs['wait_until']) |
| 92 | |
| 93 | server = cls.compute_client.servers.get(server.id) |
| 94 | cls.set_resource(name, server) |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 95 | return server |
| 96 | |
| 97 | @classmethod |
| 98 | def get_db_handle_and_meta(cls, database='nova'): |
Sean Dague | f237ccb | 2013-01-04 15:19:14 -0500 | [diff] [blame] | 99 | """Return a connection handle and metadata of an OpenStack database.""" |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 100 | engine_args = {"echo": False, |
| 101 | "convert_unicode": True, |
| 102 | "pool_recycle": 3600 |
| 103 | } |
| 104 | |
| 105 | try: |
Attila Fazekas | 3ca1fb3 | 2013-01-21 23:10:53 +0100 | [diff] [blame] | 106 | engine = create_engine(cls.config.whitebox.db_uri, **engine_args) |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 107 | connection = engine.connect() |
| 108 | meta = MetaData() |
| 109 | meta.reflect(bind=engine) |
| 110 | |
Dirk Mueller | 1db5db2 | 2013-06-23 20:21:32 +0200 | [diff] [blame] | 111 | except Exception as e: |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 112 | raise exceptions.SQLException(message=e) |
| 113 | |
| 114 | return connection, meta |
| 115 | |
| 116 | def nova_manage(self, category, action, params): |
Sean Dague | f237ccb | 2013-01-04 15:19:14 -0500 | [diff] [blame] | 117 | """Executes nova-manage command for the given action.""" |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 118 | |
| 119 | nova_manage_path = os.path.join(self.compute_bin_dir, 'nova-manage') |
| 120 | cmd = ' '.join([nova_manage_path, category, action, params]) |
| 121 | |
| 122 | if self.deploy_mode == 'devstack-local': |
| 123 | if not os.path.isdir(self.nova_dir): |
| 124 | sys.exit("Cannot find Nova source directory: %s" % |
| 125 | self.nova_dir) |
| 126 | |
| 127 | cmd = shlex.split(cmd) |
| 128 | result = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
| 129 | |
Attila Fazekas | c3a095b | 2013-08-17 09:15:44 +0200 | [diff] [blame] | 130 | # TODO(rohitk): Need to define host connection parameters in config |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 131 | else: |
| 132 | client = self.get_ssh_connection(self.config.whitebox.api_host, |
Zhongyue Luo | 79d8d36 | 2012-09-25 13:49:27 +0800 | [diff] [blame] | 133 | self.config.whitebox.api_user, |
| 134 | self.config.whitebox.api_passwd) |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 135 | result = client.exec_command(cmd) |
| 136 | |
| 137 | return result |
| 138 | |
| 139 | def get_ssh_connection(self, host, username, password): |
Sean Dague | f237ccb | 2013-01-04 15:19:14 -0500 | [diff] [blame] | 140 | """Create an SSH connection object to a host.""" |
Jay Pipes | 051075a | 2012-04-28 17:39:37 -0400 | [diff] [blame] | 141 | ssh_timeout = self.config.compute.ssh_timeout |
| 142 | ssh_client = Client(host, username, password, ssh_timeout) |
| 143 | if not ssh_client.test_connection_auth(): |
| 144 | raise exceptions.SSHTimeout() |
| 145 | else: |
| 146 | return ssh_client |