blob: f9d7a9bc9c9d42fe0e2bf53a0ee922af60b3c1cc [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
25**Usage:** ``tempest-account-generator [-h] [OPTIONS] accounts_file.yaml``.
26
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
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +030093To see help on specific argument, please do: ``tempest-account-generator
94[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,
147 admin_creds=admin_creds,
148 **credentials_factory.get_dynamic_provider_params())
sslypushenko0de7d052015-04-16 18:49:55 +0300149
150
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100151def generate_resources(cred_provider, admin):
152 # Create the list of resources to be provisioned for each process
153 # NOTE(andreaf) get_credentials expects a string for types or a list for
154 # roles. Adding all required inputs to the spec list.
155 spec = ['primary', 'alt']
Matthew Treinish36c2e282015-08-25 00:30:15 -0400156 if CONF.service_available.swift:
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100157 spec.append([CONF.object_storage.operator_role])
158 spec.append([CONF.object_storage.reseller_admin_role])
Matthew Treinish36c2e282015-08-25 00:30:15 -0400159 if CONF.service_available.heat:
Andrea Frittoli (andreaf)0abbfcb2016-06-15 23:27:29 +0100160 spec.append([CONF.orchestration.stack_owner_role,
161 CONF.object_storage.operator_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 if test_resource.network:
195 account['resources']['network'] = test_resource.network['name']
David Kranz0aa4a7b2015-06-08 13:25:41 -0400196 accounts.append(account)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100197 if os.path.exists(account_file):
198 os.rename(account_file, '.'.join((account_file, 'bak')))
199 with open(account_file, 'w') as f:
200 yaml.safe_dump(accounts, f, default_flow_style=False)
201 LOG.info('%s generated successfully!' % account_file)
sslypushenko0de7d052015-04-16 18:49:55 +0300202
203
David Paterson68b8b9d2015-12-01 15:44:14 -0800204def _parser_add_args(parser):
sslypushenko0de7d052015-04-16 18:49:55 +0300205 parser.add_argument('-c', '--config-file',
206 metavar='/etc/tempest.conf',
207 help='path to tempest config file')
208 parser.add_argument('--os-username',
209 metavar='<auth-user-name>',
210 default=os.environ.get('OS_USERNAME'),
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300211 help='User should have permissions '
sslypushenko0de7d052015-04-16 18:49:55 +0300212 'to create new user accounts and '
213 'tenants. Defaults to env[OS_USERNAME].')
214 parser.add_argument('--os-password',
215 metavar='<auth-password>',
216 default=os.environ.get('OS_PASSWORD'),
217 help='Defaults to env[OS_PASSWORD].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100218 parser.add_argument('--os-project-name',
219 metavar='<auth-project-name>',
220 default=os.environ.get('OS_PROJECT_NAME'),
221 help='Defaults to env[OS_PROJECT_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300222 parser.add_argument('--os-tenant-name',
223 metavar='<auth-tenant-name>',
224 default=os.environ.get('OS_TENANT_NAME'),
225 help='Defaults to env[OS_TENANT_NAME].')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100226 parser.add_argument('--os-domain-name',
227 metavar='<auth-domain-name>',
228 default=os.environ.get('OS_DOMAIN_NAME'),
229 help='Defaults to env[OS_DOMAIN_NAME].')
sslypushenko0de7d052015-04-16 18:49:55 +0300230 parser.add_argument('--tag',
231 default='',
232 required=False,
233 dest='tag',
234 help='Resources tag')
235 parser.add_argument('-r', '--concurrency',
236 default=1,
237 type=int,
ghanshyam46545152016-06-13 12:57:42 +0900238 required=False,
sslypushenko0de7d052015-04-16 18:49:55 +0300239 dest='concurrency',
240 help='Concurrency count')
241 parser.add_argument('--with-admin',
242 action='store_true',
243 dest='admin',
Jane Zadorozhna00fc3dc2015-05-27 18:01:56 +0300244 help='Creates admin for each concurrent group')
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100245 parser.add_argument('-i', '--identity-version',
246 default=3,
247 choices=[2, 3],
248 type=int,
249 required=False,
250 dest='identity_version',
251 help='Version of the Identity API to use')
sslypushenko0de7d052015-04-16 18:49:55 +0300252 parser.add_argument('accounts',
253 metavar='accounts_file.yaml',
254 help='Output accounts yaml file')
255
David Paterson68b8b9d2015-12-01 15:44:14 -0800256
257def get_options():
258 usage_string = ('tempest-account-generator [-h] <ARG> ...\n\n'
259 'To see help on specific argument, do:\n'
260 'tempest-account-generator <ARG> -h')
261 parser = argparse.ArgumentParser(
262 description=DESCRIPTION,
263 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
264 usage=usage_string
265 )
266
267 _parser_add_args(parser)
sslypushenko0de7d052015-04-16 18:49:55 +0300268 opts = parser.parse_args()
sslypushenko0de7d052015-04-16 18:49:55 +0300269 return opts
270
271
David Paterson68b8b9d2015-12-01 15:44:14 -0800272class TempestAccountGenerator(command.Command):
273
274 def get_parser(self, prog_name):
275 parser = super(TempestAccountGenerator, self).get_parser(prog_name)
276 _parser_add_args(parser)
277 return parser
278
279 def take_action(self, parsed_args):
280 try:
Masayuki Igawa92629432016-06-09 12:28:09 +0900281 main(parsed_args)
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
289
290
sslypushenko0de7d052015-04-16 18:49:55 +0300291def main(opts=None):
sslypushenko0de7d052015-04-16 18:49:55 +0300292 setup_logging()
David Paterson68b8b9d2015-12-01 15:44:14 -0800293 if not opts:
BinBin Congc7f7feb2016-02-17 11:22:46 +0000294 LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
295 "please use: 'tempest account-generator'")
David Paterson68b8b9d2015-12-01 15:44:14 -0800296 opts = get_options()
297 if opts.config_file:
298 config.CONF.set_config_path(opts.config_file)
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100299 if opts.os_tenant_name:
300 LOG.warning("'os-tenant-name' and 'OS_TENANT_NAME' are both "
301 "deprecated, please use 'os-project-name' or "
302 "'OS_PROJECT_NAME' instead")
303 resources = []
304 for count in range(opts.concurrency):
305 # Use N different cred_providers to obtain different sets of creds
306 cred_provider = get_credential_provider(opts)
307 resources.extend(generate_resources(cred_provider, opts.admin))
308 dump_accounts(resources, opts.identity_version, opts.accounts)
sslypushenko0de7d052015-04-16 18:49:55 +0300309
310if __name__ == "__main__":
311 main()