blob: ff552a1cbd4cc5beb176e5aac0eda95ab3b8c251 [file] [log] [blame]
sslypushenko0de7d052015-04-16 18:49:55 +03001#!/usr/bin/env python
2
3# Copyright 2015 Mirantis, Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030017"""
Masayuki Igawabbbaad62017-11-21 16:04:03 +090018Utility for creating ``accounts.yaml`` file for concurrent test runs.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030019Creates one primary user, one alt user, one swift admin, one stack owner
20and one admin (optionally) for each concurrent thread. The utility creates
Masayuki Igawabbbaad62017-11-21 16:04:03 +090021user for each tenant. The ``accounts.yaml`` file will be valid and contain
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030022credentials for created users, so each user will be in separate tenant and
23have the username, tenant_name, password and roles.
24
Masayuki Igawabbbaad62017-11-21 16:04:03 +090025**Usage:** ``tempest account-generator [-h] [OPTIONS] accounts_file.yaml``
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030026
27Positional Arguments
Matthew Treinishf45ba2e2015-08-24 15:05:01 -040028--------------------
Masayuki Igawabbbaad62017-11-21 16:04:03 +090029``accounts_file.yaml`` (Required) Provide an output accounts yaml file. Utility
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030030creates a .yaml file in the directory where the command is ran. The appropriate
31name for the file is *accounts.yaml* and it should be placed in *tempest/etc*
32directory.
33
34Authentication
35--------------
36
37Account generator creates users and tenants so it needs the admin credentials
38of your cloud to operate properly. The corresponding info can be given either
39through CLI options or environment variables.
40
Masayuki Igawa679e8642016-06-10 17:09:45 +090041You're probably familiar with these, but just to remind:
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030042
Masayuki Igawabbbaad62017-11-21 16:04:03 +090043======== ============================ ====================
44Param CLI Environment Variable
45======== ============================ ====================
46Username ``--os-username`` OS_USERNAME
47Password ``--os-password`` OS_PASSWORD
48Project ``--os-project-name`` OS_PROJECT_NAME
Masayuki Igawabbbaad62017-11-21 16:04:03 +090049Domain ``--os-domain-name`` OS_DOMAIN_NAME
50======== ============================ ====================
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030051
52Optional Arguments
Matthew Treinishf45ba2e2015-08-24 15:05:01 -040053------------------
Masayuki Igawabbbaad62017-11-21 16:04:03 +090054* ``-h, --help`` (Optional) Shows help message with the description of
55 utility and its arguments, and exits.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030056
Masayuki Igawabbbaad62017-11-21 16:04:03 +090057* ``-c, --config-file /etc/tempest.conf`` (Optional) Path
58 to tempest config file. If not specified, it searches for tempest.conf in
59 these locations:
Martin Kopec14e0be12017-11-13 12:38:12 +000060
Masayuki Igawabbbaad62017-11-21 16:04:03 +090061 - ./etc/
62 - /etc/tempest
63 - ~/.tempest/
64 - ~/
65 - /etc/
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030066
Masayuki Igawabbbaad62017-11-21 16:04:03 +090067* ``--os-username <auth-user-name>`` (Optional) Name used for authentication
68 with the OpenStack Identity service. Defaults to env[OS_USERNAME]. Note: User
69 should have permissions to create new user accounts and tenants.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030070
Masayuki Igawabbbaad62017-11-21 16:04:03 +090071* ``--os-password <auth-password>`` (Optional) Password used for authentication
72 with the OpenStack Identity service. Defaults to env[OS_PASSWORD].
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030073
Masayuki Igawabbbaad62017-11-21 16:04:03 +090074* ``--os-project-name <auth-project-name>`` (Optional) Project to request
75 authorization on. Defaults to env[OS_PROJECT_NAME].
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010076
Masayuki Igawabbbaad62017-11-21 16:04:03 +090077* ``--os-domain-name <auth-domain-name>`` (Optional) Domain the user and
78 project belong to. Defaults to env[OS_DOMAIN_NAME].
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030079
Masayuki Igawabbbaad62017-11-21 16:04:03 +090080* ``--tag TAG`` (Optional) Resources tag. Each created resource (user, project)
81 will have the prefix with the given TAG in its name. Using tag is recommended
82 for the further using, cleaning resources.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030083
Masayuki Igawabbbaad62017-11-21 16:04:03 +090084* ``-r, --concurrency CONCURRENCY`` (Optional) Concurrency count
85 (default: 1). The number of accounts required can be estimated as
86 CONCURRENCY x 2. Each user provided in *accounts.yaml* file will be in
87 a different tenant. This is required to provide isolation between test for
88 running in parallel.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030089
Masayuki Igawabbbaad62017-11-21 16:04:03 +090090* ``--with-admin`` (Optional) Creates admin for each concurrent group
91 (default: False).
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030092
Masayuki Igawabbbaad62017-11-21 16:04:03 +090093* ``-i, --identity-version VERSION`` (Optional) Provisions accounts
94 using the specified version of the identity API. (default: '3').
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010095
guo yunxian306a7de2016-10-20 18:33:21 +080096To see help on specific argument, please do: ``tempest account-generator
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030097[OPTIONS] <accounts_file.yaml> -h``.
98"""
Soniya Vyasddcd4f42019-12-24 17:55:34 +053099
Lukas Piwowarski58b05382020-06-03 12:28:53 +0000100import argparse
sslypushenko0de7d052015-04-16 18:49:55 +0300101import os
David Paterson68b8b9d2015-12-01 15:44:14 -0800102import traceback
sslypushenko0de7d052015-04-16 18:49:55 +0300103
David Paterson68b8b9d2015-12-01 15:44:14 -0800104from cliff import command
sslypushenko0de7d052015-04-16 18:49:55 +0300105from oslo_log import log as logging
106import yaml
107
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100108from tempest.common import credentials_factory
sslypushenko0de7d052015-04-16 18:49:55 +0300109from tempest import config
Matthew Treinishc51b7122017-07-17 12:28:07 -0400110from tempest.lib.common import dynamic_creds
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100111
sslypushenko0de7d052015-04-16 18:49:55 +0300112
113LOG = None
114CONF = config.CONF
David Paterson68b8b9d2015-12-01 15:44:14 -0800115DESCRIPTION = ('Create accounts.yaml file for concurrent test runs.%s'
116 'One primary user, one alt user, '
117 'one swift admin, one stack owner '
118 'and one admin (optionally) will be created '
119 'for each concurrent thread.' % os.linesep)
sslypushenko0de7d052015-04-16 18:49:55 +0300120
121
122def setup_logging():
123 global LOG
124 logging.setup(CONF, __name__)
125 LOG = logging.getLogger(__name__)
126
127
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100128def get_credential_provider(opts):
129 identity_version = "".join(['v', str(opts.identity_version)])
130 # NOTE(andreaf) For now tempest.conf controls whether resources will
131 # actually be created. Once we remove the dependency from tempest.conf
132 # we will need extra CLI option(s) to control this.
133 network_resources = {'router': True,
134 'network': True,
135 'subnet': True,
136 'dhcp': True}
137 admin_creds_dict = {'username': opts.os_username,
138 'password': opts.os_password}
Soniya Vyas195767a2019-12-24 19:20:43 +0530139 _project_name = opts.os_project_name
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100140 if opts.identity_version == 3:
141 admin_creds_dict['project_name'] = _project_name
142 admin_creds_dict['domain_name'] = opts.os_domain_name or 'Default'
143 elif opts.identity_version == 2:
144 admin_creds_dict['tenant_name'] = _project_name
145 admin_creds = credentials_factory.get_credentials(
146 fill_in=False, identity_version=identity_version, **admin_creds_dict)
147 return dynamic_creds.DynamicCredentialProvider(
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100148 name=opts.tag,
149 network_resources=network_resources,
Andrea Frittolid199caf2017-07-18 17:12:38 +0100150 **credentials_factory.get_dynamic_provider_params(
151 identity_version, admin_creds=admin_creds))
sslypushenko0de7d052015-04-16 18:49:55 +0300152
153
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100154def generate_resources(cred_provider, admin):
155 # Create the list of resources to be provisioned for each process
156 # NOTE(andreaf) get_credentials expects a string for types or a list for
157 # roles. Adding all required inputs to the spec list.
158 spec = ['primary', 'alt']
Matthew Treinish36c2e282015-08-25 00:30:15 -0400159 if CONF.service_available.swift:
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100160 spec.append([CONF.object_storage.operator_role])
161 spec.append([CONF.object_storage.reseller_admin_role])
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100162 if admin:
163 spec.append('admin')
164 resources = []
165 for cred_type in spec:
166 resources.append((cred_type, cred_provider.get_credentials(
167 credential_type=cred_type)))
sslypushenko0de7d052015-04-16 18:49:55 +0300168 return resources
169
170
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100171def dump_accounts(resources, identity_version, account_file):
sslypushenko0de7d052015-04-16 18:49:55 +0300172 accounts = []
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100173 for resource in resources:
174 cred_type, test_resource = resource
David Kranz0aa4a7b2015-06-08 13:25:41 -0400175 account = {
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100176 'username': test_resource.username,
177 'password': test_resource.password
David Kranz0aa4a7b2015-06-08 13:25:41 -0400178 }
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100179 if identity_version == 3:
180 account['project_name'] = test_resource.project_name
181 account['domain_name'] = test_resource.domain_name
182 else:
183 account['project_name'] = test_resource.tenant_name
184
185 # If the spec includes 'admin' credentials are defined via type,
186 # else they are defined via list of roles.
187 if cred_type == 'admin':
188 account['types'] = [cred_type]
189 elif cred_type not in ['primary', 'alt']:
190 account['roles'] = cred_type
191
192 if test_resource.network:
David Paterson15be99e2015-04-08 21:58:19 -0400193 account['resources'] = {}
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100194 account['resources']['network'] = test_resource.network['name']
David Kranz0aa4a7b2015-06-08 13:25:41 -0400195 accounts.append(account)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100196 if os.path.exists(account_file):
197 os.rename(account_file, '.'.join((account_file, 'bak')))
198 with open(account_file, 'w') as f:
199 yaml.safe_dump(accounts, f, default_flow_style=False)
Jordan Pittier525ec712016-12-07 17:51:26 +0100200 LOG.info('%s generated successfully!', account_file)
sslypushenko0de7d052015-04-16 18:49:55 +0300201
202
Lukas Piwowarski58b05382020-06-03 12:28:53 +0000203def positive_int(number):
204 number = int(number)
205 if number <= 0:
206 raise argparse.ArgumentTypeError("Concurrency value should be a "
207 "positive number")
208 return number
209
210
David Paterson68b8b9d2015-12-01 15:44:14 -0800211def _parser_add_args(parser):
sslypushenko0de7d052015-04-16 18:49:55 +0300212 parser.add_argument('-c', '--config-file',
213 metavar='/etc/tempest.conf',
214 help='path to tempest config file')
215 parser.add_argument('--os-username',
216 metavar='<auth-user-name>',
217 default=os.environ.get('OS_USERNAME'),
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300218 help='User should have permissions '
sslypushenko0de7d052015-04-16 18:49:55 +0300219 'to create new user accounts and '
220 'tenants. Defaults to env[OS_USERNAME].')
221 parser.add_argument('--os-password',
222 metavar='<auth-password>',
223 default=os.environ.get('OS_PASSWORD'),
224 help='Defaults to env[OS_PASSWORD].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100225 parser.add_argument('--os-project-name',
226 metavar='<auth-project-name>',
227 default=os.environ.get('OS_PROJECT_NAME'),
228 help='Defaults to env[OS_PROJECT_NAME].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100229 parser.add_argument('--os-domain-name',
230 metavar='<auth-domain-name>',
231 default=os.environ.get('OS_DOMAIN_NAME'),
232 help='Defaults to env[OS_DOMAIN_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300233 parser.add_argument('--tag',
234 default='',
235 required=False,
236 dest='tag',
237 help='Resources tag')
238 parser.add_argument('-r', '--concurrency',
239 default=1,
Lukas Piwowarski58b05382020-06-03 12:28:53 +0000240 type=positive_int,
ghanshyam46545152016-06-13 12:57:42 +0900241 required=False,
sslypushenko0de7d052015-04-16 18:49:55 +0300242 dest='concurrency',
243 help='Concurrency count')
244 parser.add_argument('--with-admin',
245 action='store_true',
246 dest='admin',
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300247 help='Creates admin for each concurrent group')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100248 parser.add_argument('-i', '--identity-version',
249 default=3,
250 choices=[2, 3],
251 type=int,
252 required=False,
253 dest='identity_version',
254 help='Version of the Identity API to use')
sslypushenko0de7d052015-04-16 18:49:55 +0300255 parser.add_argument('accounts',
256 metavar='accounts_file.yaml',
257 help='Output accounts yaml file')
258
David Paterson68b8b9d2015-12-01 15:44:14 -0800259
David Paterson68b8b9d2015-12-01 15:44:14 -0800260class TempestAccountGenerator(command.Command):
261
262 def get_parser(self, prog_name):
263 parser = super(TempestAccountGenerator, self).get_parser(prog_name)
264 _parser_add_args(parser)
265 return parser
266
267 def take_action(self, parsed_args):
268 try:
Soniya Vyasddcd4f42019-12-24 17:55:34 +0530269 if parsed_args.config_file:
270 config.CONF.set_config_path(parsed_args.config_file)
271 setup_logging()
272 resources = []
273 for count in range(parsed_args.concurrency):
274 # Use N different cred_providers to obtain different
275 # sets of creds
276 cred_provider = get_credential_provider(parsed_args)
277 resources.extend(generate_resources(cred_provider,
278 parsed_args.admin))
279 dump_accounts(resources, parsed_args.identity_version,
280 parsed_args.accounts)
281
David Paterson68b8b9d2015-12-01 15:44:14 -0800282 except Exception:
283 LOG.exception("Failure generating test accounts.")
284 traceback.print_exc()
285 raise
David Paterson68b8b9d2015-12-01 15:44:14 -0800286
287 def get_description(self):
288 return DESCRIPTION