blob: 249261b97c6e169711ef2bcdce9832e06cc6f802 [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
41You're probably familiar with these, but just to remind::
42
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +010043 +----------+---------------------------+----------------------+
44 | Param | CLI | Environment Variable |
45 +----------+---------------------------+----------------------+
46 | Username | --os-username | OS_USERNAME |
47 | Password | --os-password | OS_PASSWORD |
48 | Project | --os-project-name | OS_PROJECT_NAME |
49 | Tenant | --os-tenant-name (depr.) | OS_TENANT_NAME |
50 | Domain | --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)bd06f982016-06-02 17:26:51 +0100160 spec.append([CONF.orchestration.stack_owner_role])
161 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)
200 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,
237 required=True,
238 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():
257 usage_string = ('tempest-account-generator [-h] <ARG> ...\n\n'
258 'To see help on specific argument, do:\n'
259 'tempest-account-generator <ARG> -h')
260 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:
280 return main(parsed_args)
281 except Exception:
282 LOG.exception("Failure generating test accounts.")
283 traceback.print_exc()
284 raise
285 return 0
286
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()