blob: 172d9e156e01067341d63dc34fd40e073449da42 [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
105from tempest.common import dynamic_creds
sslypushenko0de7d052015-04-16 18:49:55 +0300106from tempest import config
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(
144 identity_version=identity_version,
145 name=opts.tag,
146 network_resources=network_resources,
Matthew Treinish75abbcf2016-10-07 16:19:12 -0400147 neutron_available=CONF.service_available.neutron,
148 create_networks=CONF.auth.create_isolated_networks,
149 identity_admin_role=CONF.identity.admin_role,
150 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
151 project_network_cidr=CONF.network.project_network_cidr,
152 project_network_mask_bits=CONF.network.project_network_mask_bits,
153 public_network_id=CONF.network.public_network_id,
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100154 admin_creds=admin_creds,
155 **credentials_factory.get_dynamic_provider_params())
sslypushenko0de7d052015-04-16 18:49:55 +0300156
157
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100158def generate_resources(cred_provider, admin):
159 # Create the list of resources to be provisioned for each process
160 # NOTE(andreaf) get_credentials expects a string for types or a list for
161 # roles. Adding all required inputs to the spec list.
162 spec = ['primary', 'alt']
Matthew Treinish36c2e282015-08-25 00:30:15 -0400163 if CONF.service_available.swift:
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100164 spec.append([CONF.object_storage.operator_role])
165 spec.append([CONF.object_storage.reseller_admin_role])
Matthew Treinish36c2e282015-08-25 00:30:15 -0400166 if CONF.service_available.heat:
Andrea Frittoli (andreaf)0abbfcb2016-06-15 23:27:29 +0100167 spec.append([CONF.orchestration.stack_owner_role,
168 CONF.object_storage.operator_role])
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100169 if admin:
170 spec.append('admin')
171 resources = []
172 for cred_type in spec:
173 resources.append((cred_type, cred_provider.get_credentials(
174 credential_type=cred_type)))
sslypushenko0de7d052015-04-16 18:49:55 +0300175 return resources
176
177
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100178def dump_accounts(resources, identity_version, account_file):
sslypushenko0de7d052015-04-16 18:49:55 +0300179 accounts = []
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100180 for resource in resources:
181 cred_type, test_resource = resource
David Kranz0aa4a7b2015-06-08 13:25:41 -0400182 account = {
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100183 'username': test_resource.username,
184 'password': test_resource.password
David Kranz0aa4a7b2015-06-08 13:25:41 -0400185 }
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100186 if identity_version == 3:
187 account['project_name'] = test_resource.project_name
188 account['domain_name'] = test_resource.domain_name
189 else:
190 account['project_name'] = test_resource.tenant_name
191
192 # If the spec includes 'admin' credentials are defined via type,
193 # else they are defined via list of roles.
194 if cred_type == 'admin':
195 account['types'] = [cred_type]
196 elif cred_type not in ['primary', 'alt']:
197 account['roles'] = cred_type
198
199 if test_resource.network:
David Paterson15be99e2015-04-08 21:58:19 -0400200 account['resources'] = {}
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100201 if test_resource.network:
202 account['resources']['network'] = test_resource.network['name']
David Kranz0aa4a7b2015-06-08 13:25:41 -0400203 accounts.append(account)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100204 if os.path.exists(account_file):
205 os.rename(account_file, '.'.join((account_file, 'bak')))
206 with open(account_file, 'w') as f:
207 yaml.safe_dump(accounts, f, default_flow_style=False)
Jordan Pittier525ec712016-12-07 17:51:26 +0100208 LOG.info('%s generated successfully!', account_file)
sslypushenko0de7d052015-04-16 18:49:55 +0300209
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].')
sslypushenko0de7d052015-04-16 18:49:55 +0300229 parser.add_argument('--os-tenant-name',
230 metavar='<auth-tenant-name>',
231 default=os.environ.get('OS_TENANT_NAME'),
232 help='Defaults to env[OS_TENANT_NAME].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100233 parser.add_argument('--os-domain-name',
234 metavar='<auth-domain-name>',
235 default=os.environ.get('OS_DOMAIN_NAME'),
236 help='Defaults to env[OS_DOMAIN_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300237 parser.add_argument('--tag',
238 default='',
239 required=False,
240 dest='tag',
241 help='Resources tag')
242 parser.add_argument('-r', '--concurrency',
243 default=1,
244 type=int,
ghanshyam46545152016-06-13 12:57:42 +0900245 required=False,
sslypushenko0de7d052015-04-16 18:49:55 +0300246 dest='concurrency',
247 help='Concurrency count')
248 parser.add_argument('--with-admin',
249 action='store_true',
250 dest='admin',
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300251 help='Creates admin for each concurrent group')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100252 parser.add_argument('-i', '--identity-version',
253 default=3,
254 choices=[2, 3],
255 type=int,
256 required=False,
257 dest='identity_version',
258 help='Version of the Identity API to use')
sslypushenko0de7d052015-04-16 18:49:55 +0300259 parser.add_argument('accounts',
260 metavar='accounts_file.yaml',
261 help='Output accounts yaml file')
262
David Paterson68b8b9d2015-12-01 15:44:14 -0800263
264def get_options():
guo yunxian306a7de2016-10-20 18:33:21 +0800265 usage_string = ('tempest account-generator [-h] <ARG> ...\n\n'
David Paterson68b8b9d2015-12-01 15:44:14 -0800266 'To see help on specific argument, do:\n'
guo yunxian306a7de2016-10-20 18:33:21 +0800267 'tempest account-generator <ARG> -h')
David Paterson68b8b9d2015-12-01 15:44:14 -0800268 parser = argparse.ArgumentParser(
269 description=DESCRIPTION,
270 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
271 usage=usage_string
272 )
273
274 _parser_add_args(parser)
sslypushenko0de7d052015-04-16 18:49:55 +0300275 opts = parser.parse_args()
sslypushenko0de7d052015-04-16 18:49:55 +0300276 return opts
277
278
David Paterson68b8b9d2015-12-01 15:44:14 -0800279class TempestAccountGenerator(command.Command):
280
281 def get_parser(self, prog_name):
282 parser = super(TempestAccountGenerator, self).get_parser(prog_name)
283 _parser_add_args(parser)
284 return parser
285
286 def take_action(self, parsed_args):
287 try:
Masayuki Igawa92629432016-06-09 12:28:09 +0900288 main(parsed_args)
David Paterson68b8b9d2015-12-01 15:44:14 -0800289 except Exception:
290 LOG.exception("Failure generating test accounts.")
291 traceback.print_exc()
292 raise
David Paterson68b8b9d2015-12-01 15:44:14 -0800293
294 def get_description(self):
295 return DESCRIPTION
296
297
sslypushenko0de7d052015-04-16 18:49:55 +0300298def main(opts=None):
sslypushenko0de7d052015-04-16 18:49:55 +0300299 setup_logging()
David Paterson68b8b9d2015-12-01 15:44:14 -0800300 if not opts:
BinBin Congc7f7feb2016-02-17 11:22:46 +0000301 LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
302 "please use: 'tempest account-generator'")
David Paterson68b8b9d2015-12-01 15:44:14 -0800303 opts = get_options()
304 if opts.config_file:
305 config.CONF.set_config_path(opts.config_file)
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
317if __name__ == "__main__":
318 main()