blob: ce918255812e8807ddb95cb1c28eb268df13c658 [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
49Tenant ``--os-tenant-name`` (depr.) OS_TENANT_NAME
50Domain ``--os-domain-name`` OS_DOMAIN_NAME
51======== ============================ ====================
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030052
53Optional Arguments
Matthew Treinishf45ba2e2015-08-24 15:05:01 -040054------------------
Masayuki Igawabbbaad62017-11-21 16:04:03 +090055* ``-h, --help`` (Optional) Shows help message with the description of
56 utility and its arguments, and exits.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030057
Masayuki Igawabbbaad62017-11-21 16:04:03 +090058* ``-c, --config-file /etc/tempest.conf`` (Optional) Path
59 to tempest config file. If not specified, it searches for tempest.conf in
60 these locations:
Martin Kopec14e0be12017-11-13 12:38:12 +000061
Masayuki Igawabbbaad62017-11-21 16:04:03 +090062 - ./etc/
63 - /etc/tempest
64 - ~/.tempest/
65 - ~/
66 - /etc/
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030067
Masayuki Igawabbbaad62017-11-21 16:04:03 +090068* ``--os-username <auth-user-name>`` (Optional) Name used for authentication
69 with the OpenStack Identity service. Defaults to env[OS_USERNAME]. Note: User
70 should have permissions to create new user accounts and tenants.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030071
Masayuki Igawabbbaad62017-11-21 16:04:03 +090072* ``--os-password <auth-password>`` (Optional) Password used for authentication
73 with the OpenStack Identity service. Defaults to env[OS_PASSWORD].
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030074
Masayuki Igawabbbaad62017-11-21 16:04:03 +090075* ``--os-project-name <auth-project-name>`` (Optional) Project to request
76 authorization on. Defaults to env[OS_PROJECT_NAME].
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010077
Masayuki Igawabbbaad62017-11-21 16:04:03 +090078* ``--os-tenant-name <auth-tenant-name>`` (Optional, deprecated) Tenant to
79 request authorization on. Defaults to env[OS_TENANT_NAME].
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010080
Masayuki Igawabbbaad62017-11-21 16:04:03 +090081* ``--os-domain-name <auth-domain-name>`` (Optional) Domain the user and
82 project belong to. Defaults to env[OS_DOMAIN_NAME].
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030083
Masayuki Igawabbbaad62017-11-21 16:04:03 +090084* ``--tag TAG`` (Optional) Resources tag. Each created resource (user, project)
85 will have the prefix with the given TAG in its name. Using tag is recommended
86 for the further using, cleaning resources.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030087
Masayuki Igawabbbaad62017-11-21 16:04:03 +090088* ``-r, --concurrency CONCURRENCY`` (Optional) Concurrency count
89 (default: 1). The number of accounts required can be estimated as
90 CONCURRENCY x 2. Each user provided in *accounts.yaml* file will be in
91 a different tenant. This is required to provide isolation between test for
92 running in parallel.
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030093
Masayuki Igawabbbaad62017-11-21 16:04:03 +090094* ``--with-admin`` (Optional) Creates admin for each concurrent group
95 (default: False).
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030096
Masayuki Igawabbbaad62017-11-21 16:04:03 +090097* ``-i, --identity-version VERSION`` (Optional) Provisions accounts
98 using the specified version of the identity API. (default: '3').
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010099
guo yunxian306a7de2016-10-20 18:33:21 +0800100To see help on specific argument, please do: ``tempest account-generator
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300101[OPTIONS] <accounts_file.yaml> -h``.
102"""
sslypushenko0de7d052015-04-16 18:49:55 +0300103import argparse
104import os
David Paterson68b8b9d2015-12-01 15:44:14 -0800105import traceback
sslypushenko0de7d052015-04-16 18:49:55 +0300106
David Paterson68b8b9d2015-12-01 15:44:14 -0800107from cliff import command
sslypushenko0de7d052015-04-16 18:49:55 +0300108from oslo_log import log as logging
109import yaml
110
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100111from tempest.common import credentials_factory
sslypushenko0de7d052015-04-16 18:49:55 +0300112from tempest import config
Matthew Treinishc51b7122017-07-17 12:28:07 -0400113from tempest.lib.common import dynamic_creds
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100114
sslypushenko0de7d052015-04-16 18:49:55 +0300115
116LOG = None
117CONF = config.CONF
David Paterson68b8b9d2015-12-01 15:44:14 -0800118DESCRIPTION = ('Create accounts.yaml file for concurrent test runs.%s'
119 'One primary user, one alt user, '
120 'one swift admin, one stack owner '
121 'and one admin (optionally) will be created '
122 'for each concurrent thread.' % os.linesep)
sslypushenko0de7d052015-04-16 18:49:55 +0300123
124
125def setup_logging():
126 global LOG
127 logging.setup(CONF, __name__)
128 LOG = logging.getLogger(__name__)
129
130
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100131def get_credential_provider(opts):
132 identity_version = "".join(['v', str(opts.identity_version)])
133 # NOTE(andreaf) For now tempest.conf controls whether resources will
134 # actually be created. Once we remove the dependency from tempest.conf
135 # we will need extra CLI option(s) to control this.
136 network_resources = {'router': True,
137 'network': True,
138 'subnet': True,
139 'dhcp': True}
140 admin_creds_dict = {'username': opts.os_username,
141 'password': opts.os_password}
142 _project_name = opts.os_project_name or opts.os_tenant_name
143 if opts.identity_version == 3:
144 admin_creds_dict['project_name'] = _project_name
145 admin_creds_dict['domain_name'] = opts.os_domain_name or 'Default'
146 elif opts.identity_version == 2:
147 admin_creds_dict['tenant_name'] = _project_name
148 admin_creds = credentials_factory.get_credentials(
149 fill_in=False, identity_version=identity_version, **admin_creds_dict)
150 return dynamic_creds.DynamicCredentialProvider(
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100151 name=opts.tag,
152 network_resources=network_resources,
Andrea Frittolid199caf2017-07-18 17:12:38 +0100153 **credentials_factory.get_dynamic_provider_params(
154 identity_version, admin_creds=admin_creds))
sslypushenko0de7d052015-04-16 18:49:55 +0300155
156
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100157def generate_resources(cred_provider, admin):
158 # Create the list of resources to be provisioned for each process
159 # NOTE(andreaf) get_credentials expects a string for types or a list for
160 # roles. Adding all required inputs to the spec list.
161 spec = ['primary', 'alt']
Matthew Treinish36c2e282015-08-25 00:30:15 -0400162 if CONF.service_available.swift:
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100163 spec.append([CONF.object_storage.operator_role])
164 spec.append([CONF.object_storage.reseller_admin_role])
ghanshyam6283daa2017-12-10 12:51:42 +0300165 spec.append([CONF.object_storage.operator_role])
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100166 if admin:
167 spec.append('admin')
168 resources = []
169 for cred_type in spec:
170 resources.append((cred_type, cred_provider.get_credentials(
171 credential_type=cred_type)))
sslypushenko0de7d052015-04-16 18:49:55 +0300172 return resources
173
174
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100175def dump_accounts(resources, identity_version, account_file):
sslypushenko0de7d052015-04-16 18:49:55 +0300176 accounts = []
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100177 for resource in resources:
178 cred_type, test_resource = resource
David Kranz0aa4a7b2015-06-08 13:25:41 -0400179 account = {
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100180 'username': test_resource.username,
181 'password': test_resource.password
David Kranz0aa4a7b2015-06-08 13:25:41 -0400182 }
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100183 if identity_version == 3:
184 account['project_name'] = test_resource.project_name
185 account['domain_name'] = test_resource.domain_name
186 else:
187 account['project_name'] = test_resource.tenant_name
188
189 # If the spec includes 'admin' credentials are defined via type,
190 # else they are defined via list of roles.
191 if cred_type == 'admin':
192 account['types'] = [cred_type]
193 elif cred_type not in ['primary', 'alt']:
194 account['roles'] = cred_type
195
196 if test_resource.network:
David Paterson15be99e2015-04-08 21:58:19 -0400197 account['resources'] = {}
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100198 if test_resource.network:
199 account['resources']['network'] = test_resource.network['name']
David Kranz0aa4a7b2015-06-08 13:25:41 -0400200 accounts.append(account)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100201 if os.path.exists(account_file):
202 os.rename(account_file, '.'.join((account_file, 'bak')))
203 with open(account_file, 'w') as f:
204 yaml.safe_dump(accounts, f, default_flow_style=False)
Jordan Pittier525ec712016-12-07 17:51:26 +0100205 LOG.info('%s generated successfully!', account_file)
sslypushenko0de7d052015-04-16 18:49:55 +0300206
207
David Paterson68b8b9d2015-12-01 15:44:14 -0800208def _parser_add_args(parser):
sslypushenko0de7d052015-04-16 18:49:55 +0300209 parser.add_argument('-c', '--config-file',
210 metavar='/etc/tempest.conf',
211 help='path to tempest config file')
212 parser.add_argument('--os-username',
213 metavar='<auth-user-name>',
214 default=os.environ.get('OS_USERNAME'),
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300215 help='User should have permissions '
sslypushenko0de7d052015-04-16 18:49:55 +0300216 'to create new user accounts and '
217 'tenants. Defaults to env[OS_USERNAME].')
218 parser.add_argument('--os-password',
219 metavar='<auth-password>',
220 default=os.environ.get('OS_PASSWORD'),
221 help='Defaults to env[OS_PASSWORD].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100222 parser.add_argument('--os-project-name',
223 metavar='<auth-project-name>',
224 default=os.environ.get('OS_PROJECT_NAME'),
225 help='Defaults to env[OS_PROJECT_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300226 parser.add_argument('--os-tenant-name',
227 metavar='<auth-tenant-name>',
228 default=os.environ.get('OS_TENANT_NAME'),
229 help='Defaults to env[OS_TENANT_NAME].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100230 parser.add_argument('--os-domain-name',
231 metavar='<auth-domain-name>',
232 default=os.environ.get('OS_DOMAIN_NAME'),
233 help='Defaults to env[OS_DOMAIN_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300234 parser.add_argument('--tag',
235 default='',
236 required=False,
237 dest='tag',
238 help='Resources tag')
239 parser.add_argument('-r', '--concurrency',
240 default=1,
241 type=int,
ghanshyam46545152016-06-13 12:57:42 +0900242 required=False,
sslypushenko0de7d052015-04-16 18:49:55 +0300243 dest='concurrency',
244 help='Concurrency count')
245 parser.add_argument('--with-admin',
246 action='store_true',
247 dest='admin',
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300248 help='Creates admin for each concurrent group')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100249 parser.add_argument('-i', '--identity-version',
250 default=3,
251 choices=[2, 3],
252 type=int,
253 required=False,
254 dest='identity_version',
255 help='Version of the Identity API to use')
sslypushenko0de7d052015-04-16 18:49:55 +0300256 parser.add_argument('accounts',
257 metavar='accounts_file.yaml',
258 help='Output accounts yaml file')
259
David Paterson68b8b9d2015-12-01 15:44:14 -0800260
261def get_options():
guo yunxian306a7de2016-10-20 18:33:21 +0800262 usage_string = ('tempest account-generator [-h] <ARG> ...\n\n'
David Paterson68b8b9d2015-12-01 15:44:14 -0800263 'To see help on specific argument, do:\n'
guo yunxian306a7de2016-10-20 18:33:21 +0800264 'tempest account-generator <ARG> -h')
David Paterson68b8b9d2015-12-01 15:44:14 -0800265 parser = argparse.ArgumentParser(
266 description=DESCRIPTION,
267 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
268 usage=usage_string
269 )
270
271 _parser_add_args(parser)
sslypushenko0de7d052015-04-16 18:49:55 +0300272 opts = parser.parse_args()
sslypushenko0de7d052015-04-16 18:49:55 +0300273 return opts
274
275
David Paterson68b8b9d2015-12-01 15:44:14 -0800276class TempestAccountGenerator(command.Command):
277
278 def get_parser(self, prog_name):
279 parser = super(TempestAccountGenerator, self).get_parser(prog_name)
280 _parser_add_args(parser)
281 return parser
282
283 def take_action(self, parsed_args):
284 try:
Masayuki Igawa92629432016-06-09 12:28:09 +0900285 main(parsed_args)
David Paterson68b8b9d2015-12-01 15:44:14 -0800286 except Exception:
287 LOG.exception("Failure generating test accounts.")
288 traceback.print_exc()
289 raise
David Paterson68b8b9d2015-12-01 15:44:14 -0800290
291 def get_description(self):
292 return DESCRIPTION
293
294
sslypushenko0de7d052015-04-16 18:49:55 +0300295def main(opts=None):
Manik Bindlish3e99c042018-06-25 06:22:31 +0000296 log_warning = False
David Paterson68b8b9d2015-12-01 15:44:14 -0800297 if not opts:
Manik Bindlish3e99c042018-06-25 06:22:31 +0000298 log_warning = True
David Paterson68b8b9d2015-12-01 15:44:14 -0800299 opts = get_options()
300 if opts.config_file:
301 config.CONF.set_config_path(opts.config_file)
Manik Bindlish3e99c042018-06-25 06:22:31 +0000302 setup_logging()
303 if log_warning:
304 LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
305 "please use: 'tempest account-generator'")
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100306 if opts.os_tenant_name:
307 LOG.warning("'os-tenant-name' and 'OS_TENANT_NAME' are both "
308 "deprecated, please use 'os-project-name' or "
309 "'OS_PROJECT_NAME' instead")
310 resources = []
311 for count in range(opts.concurrency):
312 # Use N different cred_providers to obtain different sets of creds
313 cred_provider = get_credential_provider(opts)
314 resources.extend(generate_resources(cred_provider, opts.admin))
315 dump_accounts(resources, opts.identity_version, opts.accounts)
sslypushenko0de7d052015-04-16 18:49:55 +0300316
Stephen Finucane7f4a6212018-07-06 13:58:21 +0100317
sslypushenko0de7d052015-04-16 18:49:55 +0300318if __name__ == "__main__":
319 main()