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