Joe Gordon | c97f5c7 | 2013-02-14 01:15:57 +0000 | [diff] [blame] | 1 | # Copyright 2013 OpenStack Foundation |
| 2 | # All Rights Reserved. |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 5 | # not use this file except in compliance with the License. You may obtain |
| 6 | # a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | # License for the specific language governing permissions and limitations |
| 14 | # under the License. |
| 15 | |
Matt Riedemann | 27bdb05 | 2014-07-30 14:57:55 -0700 | [diff] [blame] | 16 | import functools |
Mikhail S Medvedev | 13168d0 | 2013-06-24 16:13:40 -0500 | [diff] [blame] | 17 | import os |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 18 | import shlex |
| 19 | import subprocess |
Joe Gordon | c97f5c7 | 2013-02-14 01:15:57 +0000 | [diff] [blame] | 20 | |
Matt Riedemann | 27bdb05 | 2014-07-30 14:57:55 -0700 | [diff] [blame] | 21 | import testtools |
| 22 | |
Sean Dague | f682579 | 2013-05-08 13:51:26 -0400 | [diff] [blame] | 23 | import tempest.cli.output_parser |
Matthew Treinish | e2b56b5 | 2014-01-29 19:25:50 +0000 | [diff] [blame] | 24 | from tempest import config |
Matthew Treinish | aeb5274 | 2014-07-25 18:38:56 -0400 | [diff] [blame] | 25 | from tempest import exceptions |
Matthew Treinish | f4a9b0f | 2013-07-26 16:58:26 -0400 | [diff] [blame] | 26 | from tempest.openstack.common import log as logging |
Matt Riedemann | 27bdb05 | 2014-07-30 14:57:55 -0700 | [diff] [blame] | 27 | from tempest.openstack.common import versionutils |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 28 | import tempest.test |
Joe Gordon | c97f5c7 | 2013-02-14 01:15:57 +0000 | [diff] [blame] | 29 | |
Matthew Treinish | 90aedd1 | 2013-02-25 17:56:49 -0500 | [diff] [blame] | 30 | |
Joe Gordon | c97f5c7 | 2013-02-14 01:15:57 +0000 | [diff] [blame] | 31 | LOG = logging.getLogger(__name__) |
| 32 | |
Matthew Treinish | e2b56b5 | 2014-01-29 19:25:50 +0000 | [diff] [blame] | 33 | CONF = config.CONF |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 34 | |
| 35 | |
Matt Riedemann | 27bdb05 | 2014-07-30 14:57:55 -0700 | [diff] [blame] | 36 | def execute(cmd, action, flags='', params='', fail_ok=False, |
| 37 | merge_stderr=False): |
| 38 | """Executes specified command for the given action.""" |
| 39 | cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd), |
| 40 | flags, action, params]) |
| 41 | LOG.info("running: '%s'" % cmd) |
| 42 | cmd = shlex.split(cmd.encode('utf-8')) |
| 43 | result = '' |
| 44 | result_err = '' |
| 45 | stdout = subprocess.PIPE |
| 46 | stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE |
| 47 | proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr) |
| 48 | result, result_err = proc.communicate() |
| 49 | if not fail_ok and proc.returncode != 0: |
| 50 | raise exceptions.CommandFailed(proc.returncode, |
| 51 | cmd, |
| 52 | result, |
| 53 | result_err) |
| 54 | return result |
| 55 | |
| 56 | |
| 57 | def check_client_version(client, version): |
| 58 | """Checks if the client's version is compatible with the given version |
| 59 | |
| 60 | @param client: The client to check. |
| 61 | @param version: The version to compare against. |
| 62 | @return: True if the client version is compatible with the given version |
| 63 | parameter, False otherwise. |
| 64 | """ |
| 65 | current_version = execute(client, '', params='--version', |
| 66 | merge_stderr=True) |
| 67 | |
| 68 | if not current_version.strip(): |
| 69 | raise exceptions.TempestException('"%s --version" output was empty' % |
| 70 | client) |
| 71 | |
| 72 | return versionutils.is_compatible(version, current_version, |
| 73 | same_major=False) |
| 74 | |
| 75 | |
| 76 | def min_client_version(*args, **kwargs): |
| 77 | """A decorator to skip tests if the client used isn't of the right version. |
| 78 | |
| 79 | @param client: The client command to run. For python-novaclient, this is |
| 80 | 'nova', for python-cinderclient this is 'cinder', etc. |
| 81 | @param version: The minimum version required to run the CLI test. |
| 82 | """ |
| 83 | def decorator(func): |
| 84 | @functools.wraps(func) |
| 85 | def wrapper(*func_args, **func_kwargs): |
| 86 | if not check_client_version(kwargs['client'], kwargs['version']): |
| 87 | msg = "requires %s client version >= %s" % (kwargs['client'], |
| 88 | kwargs['version']) |
| 89 | raise testtools.TestCase.skipException(msg) |
| 90 | return func(*func_args, **func_kwargs) |
| 91 | return wrapper |
| 92 | return decorator |
| 93 | |
| 94 | |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 95 | class ClientTestBase(tempest.test.BaseTestCase): |
| 96 | @classmethod |
| 97 | def setUpClass(cls): |
| 98 | if not CONF.cli.enabled: |
| 99 | msg = "cli testing disabled" |
| 100 | raise cls.skipException(msg) |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 101 | super(ClientTestBase, cls).setUpClass() |
| 102 | |
| 103 | def __init__(self, *args, **kwargs): |
Sean Dague | f682579 | 2013-05-08 13:51:26 -0400 | [diff] [blame] | 104 | self.parser = tempest.cli.output_parser |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 105 | super(ClientTestBase, self).__init__(*args, **kwargs) |
| 106 | |
| 107 | def nova(self, action, flags='', params='', admin=True, fail_ok=False): |
| 108 | """Executes nova command for the given action.""" |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 109 | flags += ' --endpoint-type %s' % CONF.compute.endpoint_type |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 110 | return self.cmd_with_auth( |
| 111 | 'nova', action, flags, params, admin, fail_ok) |
| 112 | |
Joe Gordon | e8b0e15 | 2013-03-25 13:37:15 -0400 | [diff] [blame] | 113 | def nova_manage(self, action, flags='', params='', fail_ok=False, |
Joe Gordon | 0e7cbf8 | 2013-03-25 19:49:12 +0000 | [diff] [blame] | 114 | merge_stderr=False): |
Joe Gordon | 4edb645 | 2013-03-05 21:18:59 +0000 | [diff] [blame] | 115 | """Executes nova-manage command for the given action.""" |
Matt Riedemann | 27bdb05 | 2014-07-30 14:57:55 -0700 | [diff] [blame] | 116 | return execute( |
Joe Gordon | e8b0e15 | 2013-03-25 13:37:15 -0400 | [diff] [blame] | 117 | 'nova-manage', action, flags, params, fail_ok, merge_stderr) |
Joe Gordon | 4edb645 | 2013-03-05 21:18:59 +0000 | [diff] [blame] | 118 | |
Pavel Sedlák | 5ce5c03 | 2013-02-25 18:41:30 +0100 | [diff] [blame] | 119 | def keystone(self, action, flags='', params='', admin=True, fail_ok=False): |
| 120 | """Executes keystone command for the given action.""" |
| 121 | return self.cmd_with_auth( |
| 122 | 'keystone', action, flags, params, admin, fail_ok) |
| 123 | |
afazekas | f35f940 | 2013-03-25 14:51:13 +0100 | [diff] [blame] | 124 | def glance(self, action, flags='', params='', admin=True, fail_ok=False): |
| 125 | """Executes glance command for the given action.""" |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 126 | flags += ' --os-endpoint-type %s' % CONF.image.endpoint_type |
afazekas | f35f940 | 2013-03-25 14:51:13 +0100 | [diff] [blame] | 127 | return self.cmd_with_auth( |
| 128 | 'glance', action, flags, params, admin, fail_ok) |
| 129 | |
Mehdi Abaakouk | 8581c0b | 2013-10-04 10:45:42 +0200 | [diff] [blame] | 130 | def ceilometer(self, action, flags='', params='', admin=True, |
| 131 | fail_ok=False): |
| 132 | """Executes ceilometer command for the given action.""" |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 133 | flags += ' --os-endpoint-type %s' % CONF.telemetry.endpoint_type |
Mehdi Abaakouk | 8581c0b | 2013-10-04 10:45:42 +0200 | [diff] [blame] | 134 | return self.cmd_with_auth( |
| 135 | 'ceilometer', action, flags, params, admin, fail_ok) |
| 136 | |
Steven Hardy | 5de54ee | 2013-12-31 15:58:30 +0000 | [diff] [blame] | 137 | def heat(self, action, flags='', params='', admin=True, |
| 138 | fail_ok=False): |
| 139 | """Executes heat command for the given action.""" |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 140 | flags += ' --os-endpoint-type %s' % CONF.orchestration.endpoint_type |
Steven Hardy | 5de54ee | 2013-12-31 15:58:30 +0000 | [diff] [blame] | 141 | return self.cmd_with_auth( |
| 142 | 'heat', action, flags, params, admin, fail_ok) |
| 143 | |
saurabh | 467c411 | 2013-07-08 17:08:31 +0530 | [diff] [blame] | 144 | def cinder(self, action, flags='', params='', admin=True, fail_ok=False): |
| 145 | """Executes cinder command for the given action.""" |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 146 | flags += ' --endpoint-type %s' % CONF.volume.endpoint_type |
saurabh | 467c411 | 2013-07-08 17:08:31 +0530 | [diff] [blame] | 147 | return self.cmd_with_auth( |
| 148 | 'cinder', action, flags, params, admin, fail_ok) |
| 149 | |
vishal mahajan | 6251b78 | 2014-05-29 13:15:58 +0530 | [diff] [blame] | 150 | def swift(self, action, flags='', params='', admin=True, fail_ok=False): |
| 151 | """Executes swift command for the given action.""" |
| 152 | flags += ' --os-endpoint-type %s' % CONF.object_storage.endpoint_type |
| 153 | return self.cmd_with_auth( |
| 154 | 'swift', action, flags, params, admin, fail_ok) |
| 155 | |
saurabh | 55c29c7 | 2013-07-26 21:15:08 +0530 | [diff] [blame] | 156 | def neutron(self, action, flags='', params='', admin=True, fail_ok=False): |
| 157 | """Executes neutron command for the given action.""" |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 158 | flags += ' --endpoint-type %s' % CONF.network.endpoint_type |
saurabh | 55c29c7 | 2013-07-26 21:15:08 +0530 | [diff] [blame] | 159 | return self.cmd_with_auth( |
| 160 | 'neutron', action, flags, params, admin, fail_ok) |
| 161 | |
Ajay Yadav | acf2fda | 2014-06-18 12:01:43 +0530 | [diff] [blame] | 162 | def sahara(self, action, flags='', params='', admin=True, |
| 163 | fail_ok=False, merge_stderr=True): |
Sergey Lukjanov | 9c95a25 | 2014-03-13 23:59:22 +0400 | [diff] [blame] | 164 | """Executes sahara command for the given action.""" |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 165 | flags += ' --endpoint-type %s' % CONF.data_processing.endpoint_type |
Sergey Lukjanov | 582d1c6 | 2014-01-21 19:51:21 +0400 | [diff] [blame] | 166 | return self.cmd_with_auth( |
Ajay Yadav | acf2fda | 2014-06-18 12:01:43 +0530 | [diff] [blame] | 167 | 'sahara', action, flags, params, admin, fail_ok, merge_stderr) |
Sergey Lukjanov | 582d1c6 | 2014-01-21 19:51:21 +0400 | [diff] [blame] | 168 | |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 169 | def cmd_with_auth(self, cmd, action, flags='', params='', |
Ajay Yadav | acf2fda | 2014-06-18 12:01:43 +0530 | [diff] [blame] | 170 | admin=True, fail_ok=False, merge_stderr=False): |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 171 | """Executes given command with auth attributes appended.""" |
Attila Fazekas | c3a095b | 2013-08-17 09:15:44 +0200 | [diff] [blame] | 172 | # TODO(jogo) make admin=False work |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 173 | creds = ('--os-username %s --os-tenant-name %s --os-password %s ' |
JordanP | 71c85f6 | 2014-02-26 15:53:43 +0000 | [diff] [blame] | 174 | '--os-auth-url %s' % |
Matthew Treinish | e2b56b5 | 2014-01-29 19:25:50 +0000 | [diff] [blame] | 175 | (CONF.identity.admin_username, |
| 176 | CONF.identity.admin_tenant_name, |
| 177 | CONF.identity.admin_password, |
| 178 | CONF.identity.uri)) |
Pavel Sedlák | a2b757c | 2013-02-25 18:16:04 +0100 | [diff] [blame] | 179 | flags = creds + ' ' + flags |
Matt Riedemann | 27bdb05 | 2014-07-30 14:57:55 -0700 | [diff] [blame] | 180 | return execute(cmd, action, flags, params, fail_ok, merge_stderr) |
Pavel Sedlák | 5ce5c03 | 2013-02-25 18:41:30 +0100 | [diff] [blame] | 181 | |
| 182 | def assertTableStruct(self, items, field_names): |
| 183 | """Verify that all items has keys listed in field_names.""" |
| 184 | for item in items: |
| 185 | for field in field_names: |
| 186 | self.assertIn(field, item) |
Pavel Sedlák | 1053bd3 | 2013-04-16 16:47:40 +0200 | [diff] [blame] | 187 | |
Pavel Sedlák | 4c18fa1 | 2013-08-22 21:29:45 +0200 | [diff] [blame] | 188 | def assertFirstLineStartsWith(self, lines, beginning): |
| 189 | self.assertTrue(lines[0].startswith(beginning), |
| 190 | msg=('Beginning of first line has invalid content: %s' |
| 191 | % lines[:3])) |