blob: 8636405b45af1fe1cfdd8c236667751891de74cd [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
58**c /etc/tempest.conf**, **--config-file /etc/tempest.conf** (Optional) Path to
59tempest config file.
60
61**--os-username <auth-user-name>** (Optional) Name used for authentication with
62the OpenStack Identity service. Defaults to env[OS_USERNAME]. Note: User should
63have permissions to create new user accounts and tenants.
64
65**--os-password <auth-password>** (Optional) Password used for authentication
66with the OpenStack Identity service. Defaults to env[OS_PASSWORD].
67
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010068**--os-project-name <auth-project-name>** (Optional) Project to request
69authorization on. Defaults to env[OS_PROJECT_NAME].
70
71**--os-tenant-name <auth-tenant-name>** (Optional, deprecated) Tenant to
72request authorization on. Defaults to env[OS_TENANT_NAME].
73
74**--os-domain-name <auth-domain-name>** (Optional) Domain the user and project
75belong to. Defaults to env[OS_DOMAIN_NAME].
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030076
77**--tag TAG** (Optional) Resources tag. Each created resource (user, project)
78will have the prefix with the given TAG in its name. Using tag is recommended
79for the further using, cleaning resources.
80
81**-r CONCURRENCY**, **--concurrency CONCURRENCY** (Required) Concurrency count
82(default: 1). The number of accounts required can be estimated as
83CONCURRENCY x 2. Each user provided in *accounts.yaml* file will be in
84a different tenant. This is required to provide isolation between test for
85running in parallel.
86
87**--with-admin** (Optional) Creates admin for each concurrent group
88(default: False).
89
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010090**-i VERSION**, **--identity-version VERSION** (Optional) Provisions accounts
91using the specified version of the identity API. (default: '3').
92
guo yunxian306a7de2016-10-20 18:33:21 +080093To see help on specific argument, please do: ``tempest account-generator
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030094[OPTIONS] <accounts_file.yaml> -h``.
95"""
sslypushenko0de7d052015-04-16 18:49:55 +030096import argparse
97import os
David Paterson68b8b9d2015-12-01 15:44:14 -080098import traceback
sslypushenko0de7d052015-04-16 18:49:55 +030099
David Paterson68b8b9d2015-12-01 15:44:14 -0800100from cliff import command
sslypushenko0de7d052015-04-16 18:49:55 +0300101from oslo_log import log as logging
102import yaml
103
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100104from tempest.common import credentials_factory
sslypushenko0de7d052015-04-16 18:49:55 +0300105from tempest import config
Matthew Treinishc51b7122017-07-17 12:28:07 -0400106from tempest.lib.common import dynamic_creds
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100107
sslypushenko0de7d052015-04-16 18:49:55 +0300108
109LOG = None
110CONF = config.CONF
David Paterson68b8b9d2015-12-01 15:44:14 -0800111DESCRIPTION = ('Create accounts.yaml file for concurrent test runs.%s'
112 'One primary user, one alt user, '
113 'one swift admin, one stack owner '
114 'and one admin (optionally) will be created '
115 'for each concurrent thread.' % os.linesep)
sslypushenko0de7d052015-04-16 18:49:55 +0300116
117
118def setup_logging():
119 global LOG
120 logging.setup(CONF, __name__)
121 LOG = logging.getLogger(__name__)
122
123
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100124def get_credential_provider(opts):
125 identity_version = "".join(['v', str(opts.identity_version)])
126 # NOTE(andreaf) For now tempest.conf controls whether resources will
127 # actually be created. Once we remove the dependency from tempest.conf
128 # we will need extra CLI option(s) to control this.
129 network_resources = {'router': True,
130 'network': True,
131 'subnet': True,
132 'dhcp': True}
133 admin_creds_dict = {'username': opts.os_username,
134 'password': opts.os_password}
135 _project_name = opts.os_project_name or opts.os_tenant_name
136 if opts.identity_version == 3:
137 admin_creds_dict['project_name'] = _project_name
138 admin_creds_dict['domain_name'] = opts.os_domain_name or 'Default'
139 elif opts.identity_version == 2:
140 admin_creds_dict['tenant_name'] = _project_name
141 admin_creds = credentials_factory.get_credentials(
142 fill_in=False, identity_version=identity_version, **admin_creds_dict)
143 return dynamic_creds.DynamicCredentialProvider(
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100144 name=opts.tag,
145 network_resources=network_resources,
Andrea Frittolid199caf2017-07-18 17:12:38 +0100146 **credentials_factory.get_dynamic_provider_params(
147 identity_version, admin_creds=admin_creds))
sslypushenko0de7d052015-04-16 18:49:55 +0300148
149
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100150def generate_resources(cred_provider, admin):
151 # Create the list of resources to be provisioned for each process
152 # NOTE(andreaf) get_credentials expects a string for types or a list for
153 # roles. Adding all required inputs to the spec list.
154 spec = ['primary', 'alt']
Matthew Treinish36c2e282015-08-25 00:30:15 -0400155 if CONF.service_available.swift:
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100156 spec.append([CONF.object_storage.operator_role])
157 spec.append([CONF.object_storage.reseller_admin_role])
Matthew Treinish36c2e282015-08-25 00:30:15 -0400158 if CONF.service_available.heat:
Andrea Frittoli (andreaf)0abbfcb2016-06-15 23:27:29 +0100159 spec.append([CONF.orchestration.stack_owner_role,
160 CONF.object_storage.operator_role])
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100161 if admin:
162 spec.append('admin')
163 resources = []
164 for cred_type in spec:
165 resources.append((cred_type, cred_provider.get_credentials(
166 credential_type=cred_type)))
sslypushenko0de7d052015-04-16 18:49:55 +0300167 return resources
168
169
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100170def dump_accounts(resources, identity_version, account_file):
sslypushenko0de7d052015-04-16 18:49:55 +0300171 accounts = []
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100172 for resource in resources:
173 cred_type, test_resource = resource
David Kranz0aa4a7b2015-06-08 13:25:41 -0400174 account = {
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100175 'username': test_resource.username,
176 'password': test_resource.password
David Kranz0aa4a7b2015-06-08 13:25:41 -0400177 }
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100178 if identity_version == 3:
179 account['project_name'] = test_resource.project_name
180 account['domain_name'] = test_resource.domain_name
181 else:
182 account['project_name'] = test_resource.tenant_name
183
184 # If the spec includes 'admin' credentials are defined via type,
185 # else they are defined via list of roles.
186 if cred_type == 'admin':
187 account['types'] = [cred_type]
188 elif cred_type not in ['primary', 'alt']:
189 account['roles'] = cred_type
190
191 if test_resource.network:
David Paterson15be99e2015-04-08 21:58:19 -0400192 account['resources'] = {}
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100193 if test_resource.network:
194 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
David Paterson68b8b9d2015-12-01 15:44:14 -0800203def _parser_add_args(parser):
sslypushenko0de7d052015-04-16 18:49:55 +0300204 parser.add_argument('-c', '--config-file',
205 metavar='/etc/tempest.conf',
206 help='path to tempest config file')
207 parser.add_argument('--os-username',
208 metavar='<auth-user-name>',
209 default=os.environ.get('OS_USERNAME'),
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300210 help='User should have permissions '
sslypushenko0de7d052015-04-16 18:49:55 +0300211 'to create new user accounts and '
212 'tenants. Defaults to env[OS_USERNAME].')
213 parser.add_argument('--os-password',
214 metavar='<auth-password>',
215 default=os.environ.get('OS_PASSWORD'),
216 help='Defaults to env[OS_PASSWORD].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100217 parser.add_argument('--os-project-name',
218 metavar='<auth-project-name>',
219 default=os.environ.get('OS_PROJECT_NAME'),
220 help='Defaults to env[OS_PROJECT_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300221 parser.add_argument('--os-tenant-name',
222 metavar='<auth-tenant-name>',
223 default=os.environ.get('OS_TENANT_NAME'),
224 help='Defaults to env[OS_TENANT_NAME].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100225 parser.add_argument('--os-domain-name',
226 metavar='<auth-domain-name>',
227 default=os.environ.get('OS_DOMAIN_NAME'),
228 help='Defaults to env[OS_DOMAIN_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300229 parser.add_argument('--tag',
230 default='',
231 required=False,
232 dest='tag',
233 help='Resources tag')
234 parser.add_argument('-r', '--concurrency',
235 default=1,
236 type=int,
ghanshyam46545152016-06-13 12:57:42 +0900237 required=False,
sslypushenko0de7d052015-04-16 18:49:55 +0300238 dest='concurrency',
239 help='Concurrency count')
240 parser.add_argument('--with-admin',
241 action='store_true',
242 dest='admin',
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300243 help='Creates admin for each concurrent group')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100244 parser.add_argument('-i', '--identity-version',
245 default=3,
246 choices=[2, 3],
247 type=int,
248 required=False,
249 dest='identity_version',
250 help='Version of the Identity API to use')
sslypushenko0de7d052015-04-16 18:49:55 +0300251 parser.add_argument('accounts',
252 metavar='accounts_file.yaml',
253 help='Output accounts yaml file')
254
David Paterson68b8b9d2015-12-01 15:44:14 -0800255
256def get_options():
guo yunxian306a7de2016-10-20 18:33:21 +0800257 usage_string = ('tempest account-generator [-h] <ARG> ...\n\n'
David Paterson68b8b9d2015-12-01 15:44:14 -0800258 'To see help on specific argument, do:\n'
guo yunxian306a7de2016-10-20 18:33:21 +0800259 'tempest account-generator <ARG> -h')
David Paterson68b8b9d2015-12-01 15:44:14 -0800260 parser = argparse.ArgumentParser(
261 description=DESCRIPTION,
262 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
263 usage=usage_string
264 )
265
266 _parser_add_args(parser)
sslypushenko0de7d052015-04-16 18:49:55 +0300267 opts = parser.parse_args()
sslypushenko0de7d052015-04-16 18:49:55 +0300268 return opts
269
270
David Paterson68b8b9d2015-12-01 15:44:14 -0800271class TempestAccountGenerator(command.Command):
272
273 def get_parser(self, prog_name):
274 parser = super(TempestAccountGenerator, self).get_parser(prog_name)
275 _parser_add_args(parser)
276 return parser
277
278 def take_action(self, parsed_args):
279 try:
Masayuki Igawa92629432016-06-09 12:28:09 +0900280 main(parsed_args)
David Paterson68b8b9d2015-12-01 15:44:14 -0800281 except Exception:
282 LOG.exception("Failure generating test accounts.")
283 traceback.print_exc()
284 raise
David Paterson68b8b9d2015-12-01 15:44:14 -0800285
286 def get_description(self):
287 return DESCRIPTION
288
289
sslypushenko0de7d052015-04-16 18:49:55 +0300290def main(opts=None):
sslypushenko0de7d052015-04-16 18:49:55 +0300291 setup_logging()
David Paterson68b8b9d2015-12-01 15:44:14 -0800292 if not opts:
BinBin Congc7f7feb2016-02-17 11:22:46 +0000293 LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
294 "please use: 'tempest account-generator'")
David Paterson68b8b9d2015-12-01 15:44:14 -0800295 opts = get_options()
296 if opts.config_file:
297 config.CONF.set_config_path(opts.config_file)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100298 if opts.os_tenant_name:
299 LOG.warning("'os-tenant-name' and 'OS_TENANT_NAME' are both "
300 "deprecated, please use 'os-project-name' or "
301 "'OS_PROJECT_NAME' instead")
302 resources = []
303 for count in range(opts.concurrency):
304 # Use N different cred_providers to obtain different sets of creds
305 cred_provider = get_credential_provider(opts)
306 resources.extend(generate_resources(cred_provider, opts.admin))
307 dump_accounts(resources, opts.identity_version, opts.accounts)
sslypushenko0de7d052015-04-16 18:49:55 +0300308
309if __name__ == "__main__":
310 main()