blob: c9cffd235934602f455a88e2406b85476a63aa8d [file] [log] [blame]
Matthew Treinish9e26ca82016-02-23 11:43:20 -05001# 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
Matthew Treinish9e26ca82016-02-23 11:43:20 -050016import os
17import shlex
18import subprocess
19
Anusha Raminenif3eb9472017-01-13 08:54:01 +053020from oslo_log import log as logging
Matthew Treinish9e26ca82016-02-23 11:43:20 -050021
22from tempest.lib import base
23import tempest.lib.cli.output_parser
24from tempest.lib import exceptions
25
26
27LOG = logging.getLogger(__name__)
28
29
30def execute(cmd, action, flags='', params='', fail_ok=False,
Georgy Dyuldind95375c2016-02-24 22:05:30 +030031 merge_stderr=False, cli_dir='/usr/bin', prefix=''):
Matthew Treinish9e26ca82016-02-23 11:43:20 -050032 """Executes specified command for the given action.
33
34 :param cmd: command to be executed
35 :type cmd: string
36 :param action: string of the cli command to run
37 :type action: string
38 :param flags: any optional cli flags to use
39 :type flags: string
40 :param params: string of any optional positional args to use
41 :type params: string
42 :param fail_ok: boolean if True an exception is not raised when the
43 cli return code is non-zero
44 :type fail_ok: boolean
45 :param merge_stderr: boolean if True the stderr buffer is merged into
46 stdout
47 :type merge_stderr: boolean
48 :param cli_dir: The path where the cmd can be executed
49 :type cli_dir: string
Georgy Dyuldind95375c2016-02-24 22:05:30 +030050 :param prefix: prefix to insert before command
51 :type prefix: string
Matthew Treinish9e26ca82016-02-23 11:43:20 -050052 """
Georgy Dyuldind95375c2016-02-24 22:05:30 +030053 cmd = ' '.join([prefix, os.path.join(cli_dir, cmd),
Matthew Treinish9e26ca82016-02-23 11:43:20 -050054 flags, action, params])
Georgy Dyuldind95375c2016-02-24 22:05:30 +030055 cmd = cmd.strip()
Jordan Pittier525ec712016-12-07 17:51:26 +010056 LOG.info("running: '%s'", cmd)
Matthew Treinish9e26ca82016-02-23 11:43:20 -050057 cmd = shlex.split(cmd)
Matthew Treinish9e26ca82016-02-23 11:43:20 -050058 stdout = subprocess.PIPE
59 stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
60 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
61 result, result_err = proc.communicate()
62 if not fail_ok and proc.returncode != 0:
63 raise exceptions.CommandFailed(proc.returncode,
64 cmd,
65 result,
66 result_err)
likui7d91c872020-09-22 12:29:16 +080067 return os.fsdecode(result)
Matthew Treinish9e26ca82016-02-23 11:43:20 -050068
69
70class CLIClient(object):
71 """Class to use OpenStack official python client CLI's with auth
72
73 :param username: The username to authenticate with
74 :type username: string
75 :param password: The password to authenticate with
76 :type password: string
77 :param tenant_name: The name of the tenant to use with the client calls
78 :type tenant_name: string
79 :param uri: The auth uri for the OpenStack Deployment
80 :type uri: string
81 :param cli_dir: The path where the python client binaries are installed.
82 defaults to /usr/bin
83 :type cli_dir: string
84 :param insecure: if True, --insecure is passed to python client binaries.
85 :type insecure: boolean
Georgy Dyuldind95375c2016-02-24 22:05:30 +030086 :param prefix: prefix to insert before commands
87 :type prefix: string
Mike Fedosinb4411342017-09-26 20:17:02 +030088 :param user_domain_name: User's domain name
89 :type user_domain_name: string
90 :param user_domain_id: User's domain ID
91 :type user_domain_id: string
92 :param project_domain_name: Project's domain name
93 :type project_domain_name: string
94 :param project_domain_id: Project's domain ID
95 :type project_domain_id: string
Luigi Toscanod3db3062018-01-11 10:26:54 +010096 :param identity_api_version: Version of the Identity API
97 :type identity_api_version: string
Matthew Treinish9e26ca82016-02-23 11:43:20 -050098 """
99
Takemi Asakura30a269c2023-02-16 19:40:45 +0900100 CLIENTS_WITHOUT_IDENTITY_VERSION = ['nova', 'nova_manage', 'keystone',
101 'glance', 'ceilometer', 'heat',
102 'cinder', 'neutron', 'sahara']
103
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500104 def __init__(self, username='', password='', tenant_name='', uri='',
Mike Fedosinb4411342017-09-26 20:17:02 +0300105 cli_dir='', insecure=False, prefix='', user_domain_name=None,
106 user_domain_id=None, project_domain_name=None,
Luigi Toscanod3db3062018-01-11 10:26:54 +0100107 project_domain_id=None, identity_api_version=None, *args,
108 **kwargs):
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500109 """Initialize a new CLIClient object."""
110 super(CLIClient, self).__init__()
111 self.cli_dir = cli_dir if cli_dir else '/usr/bin'
112 self.username = username
113 self.tenant_name = tenant_name
114 self.password = password
115 self.uri = uri
116 self.insecure = insecure
Georgy Dyuldind95375c2016-02-24 22:05:30 +0300117 self.prefix = prefix
Mike Fedosinb4411342017-09-26 20:17:02 +0300118 self.user_domain_name = user_domain_name
119 self.user_domain_id = user_domain_id
120 self.project_domain_name = project_domain_name
121 self.project_domain_id = project_domain_id
Luigi Toscanod3db3062018-01-11 10:26:54 +0100122 self.identity_api_version = identity_api_version
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500123
124 def nova(self, action, flags='', params='', fail_ok=False,
125 endpoint_type='publicURL', merge_stderr=False):
126 """Executes nova command for the given action.
127
128 :param action: the cli command to run using nova
129 :type action: string
130 :param flags: any optional cli flags to use
131 :type flags: string
132 :param params: any optional positional args to use
133 :type params: string
134 :param fail_ok: if True an exception is not raised when the
135 cli return code is non-zero
136 :type fail_ok: boolean
137 :param endpoint_type: the type of endpoint for the service
138 :type endpoint_type: string
139 :param merge_stderr: if True the stderr buffer is merged into stdout
140 :type merge_stderr: boolean
141 """
Kevin_Zhengc6795b52016-05-26 19:21:19 +0800142 flags += ' --os-endpoint-type %s' % endpoint_type
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500143 return self.cmd_with_auth(
144 'nova', action, flags, params, fail_ok, merge_stderr)
145
146 def nova_manage(self, action, flags='', params='', fail_ok=False,
147 merge_stderr=False):
148 """Executes nova-manage command for the given action.
149
150 :param action: the cli command to run using nova-manage
151 :type action: string
152 :param flags: any optional cli flags to use
153 :type flags: string
154 :param params: any optional positional args to use
155 :type params: string
156 :param fail_ok: if True an exception is not raised when the
157 cli return code is non-zero
158 :type fail_ok: boolean
159 :param merge_stderr: if True the stderr buffer is merged into stdout
160 :type merge_stderr: boolean
161 """
162 return execute(
163 'nova-manage', action, flags, params, fail_ok, merge_stderr,
164 self.cli_dir)
165
166 def keystone(self, action, flags='', params='', fail_ok=False,
167 merge_stderr=False):
168 """Executes keystone command for the given action.
169
170 :param action: the cli command to run using keystone
171 :type action: string
172 :param flags: any optional cli flags to use
173 :type flags: string
174 :param params: any optional positional args to use
175 :type params: string
176 :param fail_ok: if True an exception is not raised when the
177 cli return code is non-zero
178 :type fail_ok: boolean
179 :param merge_stderr: if True the stderr buffer is merged into stdout
180 :type merge_stderr: boolean
181 """
182 return self.cmd_with_auth(
183 'keystone', action, flags, params, fail_ok, merge_stderr)
184
185 def glance(self, action, flags='', params='', fail_ok=False,
186 endpoint_type='publicURL', merge_stderr=False):
187 """Executes glance command for the given action.
188
189 :param action: the cli command to run using glance
190 :type action: string
191 :param flags: any optional cli flags to use
192 :type flags: string
193 :param params: any optional positional args to use
194 :type params: string
195 :param fail_ok: if True an exception is not raised when the
196 cli return code is non-zero
197 :type fail_ok: boolean
198 :param endpoint_type: the type of endpoint for the service
199 :type endpoint_type: string
200 :param merge_stderr: if True the stderr buffer is merged into stdout
201 :type merge_stderr: boolean
202 """
203 flags += ' --os-endpoint-type %s' % endpoint_type
204 return self.cmd_with_auth(
205 'glance', action, flags, params, fail_ok, merge_stderr)
206
207 def ceilometer(self, action, flags='', params='',
208 fail_ok=False, endpoint_type='publicURL',
209 merge_stderr=False):
210 """Executes ceilometer command for the given action.
211
212 :param action: the cli command to run using ceilometer
213 :type action: string
214 :param flags: any optional cli flags to use
215 :type flags: string
216 :param params: any optional positional args to use
217 :type params: string
218 :param fail_ok: if True an exception is not raised when the
219 cli return code is non-zero
220 :type fail_ok: boolean
221 :param endpoint_type: the type of endpoint for the service
222 :type endpoint_type: string
223 :param merge_stderr: if True the stderr buffer is merged into stdout
224 :type merge_stderr: boolean
225 """
226 flags += ' --os-endpoint-type %s' % endpoint_type
227 return self.cmd_with_auth(
228 'ceilometer', action, flags, params, fail_ok, merge_stderr)
229
230 def heat(self, action, flags='', params='',
231 fail_ok=False, endpoint_type='publicURL', merge_stderr=False):
232 """Executes heat command for the given action.
233
234 :param action: the cli command to run using heat
235 :type action: string
236 :param flags: any optional cli flags to use
237 :type flags: string
238 :param params: any optional positional args to use
239 :type params: string
240 :param fail_ok: if True an exception is not raised when the
241 cli return code is non-zero
242 :type fail_ok: boolean
243 :param endpoint_type: the type of endpoint for the service
244 :type endpoint_type: string
245 :param merge_stderr: if True the stderr buffer is merged into stdout
246 :type merge_stderr: boolean
247 """
248 flags += ' --os-endpoint-type %s' % endpoint_type
249 return self.cmd_with_auth(
250 'heat', action, flags, params, fail_ok, merge_stderr)
251
252 def cinder(self, action, flags='', params='', fail_ok=False,
253 endpoint_type='publicURL', merge_stderr=False):
254 """Executes cinder command for the given action.
255
256 :param action: the cli command to run using cinder
257 :type action: string
258 :param flags: any optional cli flags to use
259 :type flags: string
260 :param params: any optional positional args to use
261 :type params: string
262 :param fail_ok: if True an exception is not raised when the
263 cli return code is non-zero
264 :type fail_ok: boolean
265 :param endpoint_type: the type of endpoint for the service
266 :type endpoint_type: string
267 :param merge_stderr: if True the stderr buffer is merged into stdout
268 :type merge_stderr: boolean
269 """
Sean McGinnis2e354d72018-07-26 15:27:31 -0500270 flags += ' --os-endpoint-type %s' % endpoint_type
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500271 return self.cmd_with_auth(
272 'cinder', action, flags, params, fail_ok, merge_stderr)
273
274 def swift(self, action, flags='', params='', fail_ok=False,
275 endpoint_type='publicURL', merge_stderr=False):
276 """Executes swift command for the given action.
277
278 :param action: the cli command to run using swift
279 :type action: string
280 :param flags: any optional cli flags to use
281 :type flags: string
282 :param params: any optional positional args to use
283 :type params: string
284 :param fail_ok: if True an exception is not raised when the
285 cli return code is non-zero
286 :type fail_ok: boolean
287 :param endpoint_type: the type of endpoint for the service
288 :type endpoint_type: string
289 :param merge_stderr: if True the stderr buffer is merged into stdout
290 :type merge_stderr: boolean
291 """
292 flags += ' --os-endpoint-type %s' % endpoint_type
293 return self.cmd_with_auth(
294 'swift', action, flags, params, fail_ok, merge_stderr)
295
296 def neutron(self, action, flags='', params='', fail_ok=False,
297 endpoint_type='publicURL', merge_stderr=False):
298 """Executes neutron command for the given action.
299
300 :param action: the cli command to run using neutron
301 :type action: string
302 :param flags: any optional cli flags to use
303 :type flags: string
304 :param params: any optional positional args to use
305 :type params: string
306 :param fail_ok: if True an exception is not raised when the
307 cli return code is non-zero
308 :type fail_ok: boolean
309 :param endpoint_type: the type of endpoint for the service
310 :type endpoint_type: string
311 :param merge_stderr: if True the stderr buffer is merged into stdout
312 :type merge_stderr: boolean
313 """
314 flags += ' --endpoint-type %s' % endpoint_type
315 return self.cmd_with_auth(
316 'neutron', action, flags, params, fail_ok, merge_stderr)
317
318 def sahara(self, action, flags='', params='',
319 fail_ok=False, endpoint_type='publicURL', merge_stderr=True):
320 """Executes sahara command for the given action.
321
322 :param action: the cli command to run using sahara
323 :type action: string
324 :param flags: any optional cli flags to use
325 :type flags: string
326 :param params: any optional positional args to use
327 :type params: string
328 :param fail_ok: if True an exception is not raised when the
329 cli return code is non-zero
330 :type fail_ok: boolean
331 :param endpoint_type: the type of endpoint for the service
332 :type endpoint_type: string
333 :param merge_stderr: if True the stderr buffer is merged into stdout
334 :type merge_stderr: boolean
335 """
336 flags += ' --endpoint-type %s' % endpoint_type
337 return self.cmd_with_auth(
338 'sahara', action, flags, params, fail_ok, merge_stderr)
339
340 def openstack(self, action, flags='', params='', fail_ok=False,
341 merge_stderr=False):
342 """Executes openstack command for the given action.
343
344 :param action: the cli command to run using openstack
345 :type action: string
346 :param flags: any optional cli flags to use
347 :type flags: string
348 :param params: any optional positional args to use
349 :type params: string
350 :param fail_ok: if True an exception is not raised when the
351 cli return code is non-zero
352 :type fail_ok: boolean
353 :param merge_stderr: if True the stderr buffer is merged into stdout
354 :type merge_stderr: boolean
355 """
356 return self.cmd_with_auth(
357 'openstack', action, flags, params, fail_ok, merge_stderr)
358
359 def cmd_with_auth(self, cmd, action, flags='', params='',
360 fail_ok=False, merge_stderr=False):
361 """Executes given command with auth attributes appended.
362
363 :param cmd: command to be executed
364 :type cmd: string
365 :param action: command on cli to run
366 :type action: string
367 :param flags: optional cli flags to use
368 :type flags: string
369 :param params: optional positional args to use
370 :type params: string
371 :param fail_ok: if True an exception is not raised when the cli return
372 code is non-zero
373 :type fail_ok: boolean
374 :param merge_stderr: if True the stderr buffer is merged into stdout
375 :type merge_stderr: boolean
376 """
Luigi Toscanod3db3062018-01-11 10:26:54 +0100377 creds = ('--os-username %s --os-project-name %s --os-password %s '
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500378 '--os-auth-url %s' %
379 (self.username,
380 self.tenant_name,
381 self.password,
382 self.uri))
Luigi Toscanod3db3062018-01-11 10:26:54 +0100383 if self.identity_api_version:
Takemi Asakura30a269c2023-02-16 19:40:45 +0900384 if cmd not in self.CLIENTS_WITHOUT_IDENTITY_VERSION:
385 creds += ' --os-identity-api-version %s' % (
386 self.identity_api_version)
Mike Fedosinb4411342017-09-26 20:17:02 +0300387 if self.user_domain_name is not None:
388 creds += ' --os-user-domain-name %s' % self.user_domain_name
389 if self.user_domain_id is not None:
390 creds += ' --os-user-domain-id %s' % self.user_domain_id
391 if self.project_domain_name is not None:
392 creds += ' --os-project-domain-name %s' % self.project_domain_name
393 if self.project_domain_id is not None:
394 creds += ' --os-project-domain-id %s' % self.project_domain_id
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500395 if self.insecure:
396 flags = creds + ' --insecure ' + flags
397 else:
398 flags = creds + ' ' + flags
399 return execute(cmd, action, flags, params, fail_ok, merge_stderr,
Georgy Dyuldind95375c2016-02-24 22:05:30 +0300400 self.cli_dir, prefix=self.prefix)
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500401
402
403class ClientTestBase(base.BaseTestCase):
404 """Base test class for testing the OpenStack client CLI interfaces."""
405
406 def setUp(self):
407 super(ClientTestBase, self).setUp()
408 self.clients = self._get_clients()
409 self.parser = tempest.lib.cli.output_parser
410
411 def _get_clients(self):
412 """Abstract method to initialize CLIClient object.
413
414 This method must be overloaded in child test classes. It should be
415 used to initialize the CLIClient object with the appropriate
416 credentials during the setUp() phase of tests.
417 """
418 raise NotImplementedError
419
420 def assertTableStruct(self, items, field_names):
421 """Verify that all items has keys listed in field_names.
422
423 :param items: items to assert are field names in the output table
424 :type items: list
425 :param field_names: field names from the output table of the cmd
426 :type field_names: list
427 """
428 for item in items:
429 for field in field_names:
430 self.assertIn(field, item)
431
432 def assertFirstLineStartsWith(self, lines, beginning):
433 """Verify that the first line starts with a string
434
435 :param lines: strings for each line of output
436 :type lines: list
437 :param beginning: verify this is at the beginning of the first line
438 :type beginning: string
439 """
440 self.assertTrue(lines[0].startswith(beginning),
441 msg=('Beginning of first line has invalid content: %s'
442 % lines[:3]))