blob: c2f8627ec7c2296d1da85d849940a847f0085836 [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"""
18Utility for creating **accounts.yaml** file for concurrent test runs.
19Creates one primary user, one alt user, one swift admin, one stack owner
20and one admin (optionally) for each concurrent thread. The utility creates
21user for each tenant. The **accounts.yaml** file will be valid and contain
22credentials for created users, so each user will be in separate tenant and
23have the username, tenant_name, password and roles.
24
guo yunxian306a7de2016-10-20 18:33:21 +080025**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--------------------
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030029**accounts_file.yaml** (Required) Provide an output accounts yaml file. Utility
30creates 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 Igawa679e8642016-06-10 17:09:45 +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------------------
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030055**-h**, **--help** (Optional) Shows help message with the description of
56utility and its arguments, and exits.
57
Martin Kopec14e0be12017-11-13 12:38:12 +000058**-c /etc/tempest.conf**, **--config-file /etc/tempest.conf** (Optional) Path
59to tempest config file. If not specified, it searches for tempest.conf in these
60locations:
61
62- ./etc/
63- /etc/tempest
64- ~/.tempest/
65- ~/
66- /etc/
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030067
68**--os-username <auth-user-name>** (Optional) Name used for authentication with
69the OpenStack Identity service. Defaults to env[OS_USERNAME]. Note: User should
70have permissions to create new user accounts and tenants.
71
72**--os-password <auth-password>** (Optional) Password used for authentication
73with the OpenStack Identity service. Defaults to env[OS_PASSWORD].
74
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010075**--os-project-name <auth-project-name>** (Optional) Project to request
76authorization on. Defaults to env[OS_PROJECT_NAME].
77
78**--os-tenant-name <auth-tenant-name>** (Optional, deprecated) Tenant to
79request authorization on. Defaults to env[OS_TENANT_NAME].
80
81**--os-domain-name <auth-domain-name>** (Optional) Domain the user and project
82belong to. Defaults to env[OS_DOMAIN_NAME].
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030083
84**--tag TAG** (Optional) Resources tag. Each created resource (user, project)
85will have the prefix with the given TAG in its name. Using tag is recommended
86for the further using, cleaning resources.
87
Martin Kopec14e0be12017-11-13 12:38:12 +000088**-r CONCURRENCY**, **--concurrency CONCURRENCY** (Optional) Concurrency count
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030089(default: 1). The number of accounts required can be estimated as
90CONCURRENCY x 2. Each user provided in *accounts.yaml* file will be in
91a different tenant. This is required to provide isolation between test for
92running in parallel.
93
94**--with-admin** (Optional) Creates admin for each concurrent group
95(default: False).
96
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010097**-i VERSION**, **--identity-version VERSION** (Optional) Provisions accounts
98using the specified version of the identity API. (default: '3').
99
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])
Matthew Treinish36c2e282015-08-25 00:30:15 -0400165 if CONF.service_available.heat:
Andrea Frittoli (andreaf)0abbfcb2016-06-15 23:27:29 +0100166 spec.append([CONF.orchestration.stack_owner_role,
167 CONF.object_storage.operator_role])
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100168 if admin:
169 spec.append('admin')
170 resources = []
171 for cred_type in spec:
172 resources.append((cred_type, cred_provider.get_credentials(
173 credential_type=cred_type)))
sslypushenko0de7d052015-04-16 18:49:55 +0300174 return resources
175
176
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100177def dump_accounts(resources, identity_version, account_file):
sslypushenko0de7d052015-04-16 18:49:55 +0300178 accounts = []
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100179 for resource in resources:
180 cred_type, test_resource = resource
David Kranz0aa4a7b2015-06-08 13:25:41 -0400181 account = {
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100182 'username': test_resource.username,
183 'password': test_resource.password
David Kranz0aa4a7b2015-06-08 13:25:41 -0400184 }
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100185 if identity_version == 3:
186 account['project_name'] = test_resource.project_name
187 account['domain_name'] = test_resource.domain_name
188 else:
189 account['project_name'] = test_resource.tenant_name
190
191 # If the spec includes 'admin' credentials are defined via type,
192 # else they are defined via list of roles.
193 if cred_type == 'admin':
194 account['types'] = [cred_type]
195 elif cred_type not in ['primary', 'alt']:
196 account['roles'] = cred_type
197
198 if test_resource.network:
David Paterson15be99e2015-04-08 21:58:19 -0400199 account['resources'] = {}
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100200 if test_resource.network:
201 account['resources']['network'] = test_resource.network['name']
David Kranz0aa4a7b2015-06-08 13:25:41 -0400202 accounts.append(account)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100203 if os.path.exists(account_file):
204 os.rename(account_file, '.'.join((account_file, 'bak')))
205 with open(account_file, 'w') as f:
206 yaml.safe_dump(accounts, f, default_flow_style=False)
Jordan Pittier525ec712016-12-07 17:51:26 +0100207 LOG.info('%s generated successfully!', account_file)
sslypushenko0de7d052015-04-16 18:49:55 +0300208
209
David Paterson68b8b9d2015-12-01 15:44:14 -0800210def _parser_add_args(parser):
sslypushenko0de7d052015-04-16 18:49:55 +0300211 parser.add_argument('-c', '--config-file',
212 metavar='/etc/tempest.conf',
213 help='path to tempest config file')
214 parser.add_argument('--os-username',
215 metavar='<auth-user-name>',
216 default=os.environ.get('OS_USERNAME'),
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300217 help='User should have permissions '
sslypushenko0de7d052015-04-16 18:49:55 +0300218 'to create new user accounts and '
219 'tenants. Defaults to env[OS_USERNAME].')
220 parser.add_argument('--os-password',
221 metavar='<auth-password>',
222 default=os.environ.get('OS_PASSWORD'),
223 help='Defaults to env[OS_PASSWORD].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100224 parser.add_argument('--os-project-name',
225 metavar='<auth-project-name>',
226 default=os.environ.get('OS_PROJECT_NAME'),
227 help='Defaults to env[OS_PROJECT_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300228 parser.add_argument('--os-tenant-name',
229 metavar='<auth-tenant-name>',
230 default=os.environ.get('OS_TENANT_NAME'),
231 help='Defaults to env[OS_TENANT_NAME].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100232 parser.add_argument('--os-domain-name',
233 metavar='<auth-domain-name>',
234 default=os.environ.get('OS_DOMAIN_NAME'),
235 help='Defaults to env[OS_DOMAIN_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300236 parser.add_argument('--tag',
237 default='',
238 required=False,
239 dest='tag',
240 help='Resources tag')
241 parser.add_argument('-r', '--concurrency',
242 default=1,
243 type=int,
ghanshyam46545152016-06-13 12:57:42 +0900244 required=False,
sslypushenko0de7d052015-04-16 18:49:55 +0300245 dest='concurrency',
246 help='Concurrency count')
247 parser.add_argument('--with-admin',
248 action='store_true',
249 dest='admin',
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300250 help='Creates admin for each concurrent group')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100251 parser.add_argument('-i', '--identity-version',
252 default=3,
253 choices=[2, 3],
254 type=int,
255 required=False,
256 dest='identity_version',
257 help='Version of the Identity API to use')
sslypushenko0de7d052015-04-16 18:49:55 +0300258 parser.add_argument('accounts',
259 metavar='accounts_file.yaml',
260 help='Output accounts yaml file')
261
David Paterson68b8b9d2015-12-01 15:44:14 -0800262
263def get_options():
guo yunxian306a7de2016-10-20 18:33:21 +0800264 usage_string = ('tempest account-generator [-h] <ARG> ...\n\n'
David Paterson68b8b9d2015-12-01 15:44:14 -0800265 'To see help on specific argument, do:\n'
guo yunxian306a7de2016-10-20 18:33:21 +0800266 'tempest account-generator <ARG> -h')
David Paterson68b8b9d2015-12-01 15:44:14 -0800267 parser = argparse.ArgumentParser(
268 description=DESCRIPTION,
269 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
270 usage=usage_string
271 )
272
273 _parser_add_args(parser)
sslypushenko0de7d052015-04-16 18:49:55 +0300274 opts = parser.parse_args()
sslypushenko0de7d052015-04-16 18:49:55 +0300275 return opts
276
277
David Paterson68b8b9d2015-12-01 15:44:14 -0800278class TempestAccountGenerator(command.Command):
279
280 def get_parser(self, prog_name):
281 parser = super(TempestAccountGenerator, self).get_parser(prog_name)
282 _parser_add_args(parser)
283 return parser
284
285 def take_action(self, parsed_args):
286 try:
Masayuki Igawa92629432016-06-09 12:28:09 +0900287 main(parsed_args)
David Paterson68b8b9d2015-12-01 15:44:14 -0800288 except Exception:
289 LOG.exception("Failure generating test accounts.")
290 traceback.print_exc()
291 raise
David Paterson68b8b9d2015-12-01 15:44:14 -0800292
293 def get_description(self):
294 return DESCRIPTION
295
296
sslypushenko0de7d052015-04-16 18:49:55 +0300297def main(opts=None):
sslypushenko0de7d052015-04-16 18:49:55 +0300298 setup_logging()
David Paterson68b8b9d2015-12-01 15:44:14 -0800299 if not opts:
BinBin Congc7f7feb2016-02-17 11:22:46 +0000300 LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
301 "please use: 'tempest account-generator'")
David Paterson68b8b9d2015-12-01 15:44:14 -0800302 opts = get_options()
303 if opts.config_file:
304 config.CONF.set_config_path(opts.config_file)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100305 if opts.os_tenant_name:
306 LOG.warning("'os-tenant-name' and 'OS_TENANT_NAME' are both "
307 "deprecated, please use 'os-project-name' or "
308 "'OS_PROJECT_NAME' instead")
309 resources = []
310 for count in range(opts.concurrency):
311 # Use N different cred_providers to obtain different sets of creds
312 cred_provider = get_credential_provider(opts)
313 resources.extend(generate_resources(cred_provider, opts.admin))
314 dump_accounts(resources, opts.identity_version, opts.accounts)
sslypushenko0de7d052015-04-16 18:49:55 +0300315
316if __name__ == "__main__":
317 main()