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