sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 1 | #!/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 Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 17 | """ |
| 18 | Utility for creating **accounts.yaml** file for concurrent test runs. |
| 19 | Creates one primary user, one alt user, one swift admin, one stack owner |
| 20 | and one admin (optionally) for each concurrent thread. The utility creates |
| 21 | user for each tenant. The **accounts.yaml** file will be valid and contain |
| 22 | credentials for created users, so each user will be in separate tenant and |
| 23 | have the username, tenant_name, password and roles. |
| 24 | |
guo yunxian | 306a7de | 2016-10-20 18:33:21 +0800 | [diff] [blame] | 25 | **Usage:** ``tempest account-generator [-h] [OPTIONS] accounts_file.yaml``. |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 26 | |
| 27 | Positional Arguments |
Matthew Treinish | f45ba2e | 2015-08-24 15:05:01 -0400 | [diff] [blame] | 28 | -------------------- |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 29 | **accounts_file.yaml** (Required) Provide an output accounts yaml file. Utility |
| 30 | creates a .yaml file in the directory where the command is ran. The appropriate |
| 31 | name for the file is *accounts.yaml* and it should be placed in *tempest/etc* |
| 32 | directory. |
| 33 | |
| 34 | Authentication |
| 35 | -------------- |
| 36 | |
| 37 | Account generator creates users and tenants so it needs the admin credentials |
| 38 | of your cloud to operate properly. The corresponding info can be given either |
| 39 | through CLI options or environment variables. |
| 40 | |
Masayuki Igawa | 679e864 | 2016-06-10 17:09:45 +0900 | [diff] [blame] | 41 | You're probably familiar with these, but just to remind: |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 42 | |
Masayuki Igawa | 679e864 | 2016-06-10 17:09:45 +0900 | [diff] [blame] | 43 | ======== ======================== ==================== |
| 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 Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 52 | |
| 53 | Optional Arguments |
Matthew Treinish | f45ba2e | 2015-08-24 15:05:01 -0400 | [diff] [blame] | 54 | ------------------ |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 55 | **-h**, **--help** (Optional) Shows help message with the description of |
| 56 | utility and its arguments, and exits. |
| 57 | |
Martin Kopec | 14e0be1 | 2017-11-13 12:38:12 +0000 | [diff] [blame] | 58 | **-c /etc/tempest.conf**, **--config-file /etc/tempest.conf** (Optional) Path |
| 59 | to tempest config file. If not specified, it searches for tempest.conf in these |
| 60 | locations: |
| 61 | |
| 62 | - ./etc/ |
| 63 | - /etc/tempest |
| 64 | - ~/.tempest/ |
| 65 | - ~/ |
| 66 | - /etc/ |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 67 | |
| 68 | **--os-username <auth-user-name>** (Optional) Name used for authentication with |
| 69 | the OpenStack Identity service. Defaults to env[OS_USERNAME]. Note: User should |
| 70 | have permissions to create new user accounts and tenants. |
| 71 | |
| 72 | **--os-password <auth-password>** (Optional) Password used for authentication |
| 73 | with the OpenStack Identity service. Defaults to env[OS_PASSWORD]. |
| 74 | |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 75 | **--os-project-name <auth-project-name>** (Optional) Project to request |
| 76 | authorization on. Defaults to env[OS_PROJECT_NAME]. |
| 77 | |
| 78 | **--os-tenant-name <auth-tenant-name>** (Optional, deprecated) Tenant to |
| 79 | request authorization on. Defaults to env[OS_TENANT_NAME]. |
| 80 | |
| 81 | **--os-domain-name <auth-domain-name>** (Optional) Domain the user and project |
| 82 | belong to. Defaults to env[OS_DOMAIN_NAME]. |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 83 | |
| 84 | **--tag TAG** (Optional) Resources tag. Each created resource (user, project) |
| 85 | will have the prefix with the given TAG in its name. Using tag is recommended |
| 86 | for the further using, cleaning resources. |
| 87 | |
Martin Kopec | 14e0be1 | 2017-11-13 12:38:12 +0000 | [diff] [blame] | 88 | **-r CONCURRENCY**, **--concurrency CONCURRENCY** (Optional) Concurrency count |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 89 | (default: 1). The number of accounts required can be estimated as |
| 90 | CONCURRENCY x 2. Each user provided in *accounts.yaml* file will be in |
| 91 | a different tenant. This is required to provide isolation between test for |
| 92 | running in parallel. |
| 93 | |
| 94 | **--with-admin** (Optional) Creates admin for each concurrent group |
| 95 | (default: False). |
| 96 | |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 97 | **-i VERSION**, **--identity-version VERSION** (Optional) Provisions accounts |
| 98 | using the specified version of the identity API. (default: '3'). |
| 99 | |
guo yunxian | 306a7de | 2016-10-20 18:33:21 +0800 | [diff] [blame] | 100 | To see help on specific argument, please do: ``tempest account-generator |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 101 | [OPTIONS] <accounts_file.yaml> -h``. |
| 102 | """ |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 103 | import argparse |
| 104 | import os |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 105 | import traceback |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 106 | |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 107 | from cliff import command |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 108 | from oslo_log import log as logging |
| 109 | import yaml |
| 110 | |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 111 | from tempest.common import credentials_factory |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 112 | from tempest import config |
Matthew Treinish | c51b712 | 2017-07-17 12:28:07 -0400 | [diff] [blame] | 113 | from tempest.lib.common import dynamic_creds |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 114 | |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 115 | |
| 116 | LOG = None |
| 117 | CONF = config.CONF |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 118 | DESCRIPTION = ('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) |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 123 | |
| 124 | |
| 125 | def setup_logging(): |
| 126 | global LOG |
| 127 | logging.setup(CONF, __name__) |
| 128 | LOG = logging.getLogger(__name__) |
| 129 | |
| 130 | |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 131 | def 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) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 151 | name=opts.tag, |
| 152 | network_resources=network_resources, |
Andrea Frittoli | d199caf | 2017-07-18 17:12:38 +0100 | [diff] [blame] | 153 | **credentials_factory.get_dynamic_provider_params( |
| 154 | identity_version, admin_creds=admin_creds)) |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 155 | |
| 156 | |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 157 | def 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 Treinish | 36c2e28 | 2015-08-25 00:30:15 -0400 | [diff] [blame] | 162 | if CONF.service_available.swift: |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 163 | spec.append([CONF.object_storage.operator_role]) |
| 164 | spec.append([CONF.object_storage.reseller_admin_role]) |
Matthew Treinish | 36c2e28 | 2015-08-25 00:30:15 -0400 | [diff] [blame] | 165 | if CONF.service_available.heat: |
Andrea Frittoli (andreaf) | 0abbfcb | 2016-06-15 23:27:29 +0100 | [diff] [blame] | 166 | spec.append([CONF.orchestration.stack_owner_role, |
| 167 | CONF.object_storage.operator_role]) |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 168 | 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))) |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 174 | return resources |
| 175 | |
| 176 | |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 177 | def dump_accounts(resources, identity_version, account_file): |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 178 | accounts = [] |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 179 | for resource in resources: |
| 180 | cred_type, test_resource = resource |
David Kranz | 0aa4a7b | 2015-06-08 13:25:41 -0400 | [diff] [blame] | 181 | account = { |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 182 | 'username': test_resource.username, |
| 183 | 'password': test_resource.password |
David Kranz | 0aa4a7b | 2015-06-08 13:25:41 -0400 | [diff] [blame] | 184 | } |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 185 | 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 Paterson | 15be99e | 2015-04-08 21:58:19 -0400 | [diff] [blame] | 199 | account['resources'] = {} |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 200 | if test_resource.network: |
| 201 | account['resources']['network'] = test_resource.network['name'] |
David Kranz | 0aa4a7b | 2015-06-08 13:25:41 -0400 | [diff] [blame] | 202 | accounts.append(account) |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 203 | 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 Pittier | 525ec71 | 2016-12-07 17:51:26 +0100 | [diff] [blame] | 207 | LOG.info('%s generated successfully!', account_file) |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 208 | |
| 209 | |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 210 | def _parser_add_args(parser): |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 211 | 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 Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 217 | help='User should have permissions ' |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 218 | '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) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 224 | 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].') |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 228 | 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) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 232 | 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].') |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 236 | 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, |
ghanshyam | 4654515 | 2016-06-13 12:57:42 +0900 | [diff] [blame] | 244 | required=False, |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 245 | dest='concurrency', |
| 246 | help='Concurrency count') |
| 247 | parser.add_argument('--with-admin', |
| 248 | action='store_true', |
| 249 | dest='admin', |
Jane Zadorozhna | 00fc3dc | 2015-05-27 18:01:56 +0300 | [diff] [blame] | 250 | help='Creates admin for each concurrent group') |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 251 | 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') |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 258 | parser.add_argument('accounts', |
| 259 | metavar='accounts_file.yaml', |
| 260 | help='Output accounts yaml file') |
| 261 | |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 262 | |
| 263 | def get_options(): |
guo yunxian | 306a7de | 2016-10-20 18:33:21 +0800 | [diff] [blame] | 264 | usage_string = ('tempest account-generator [-h] <ARG> ...\n\n' |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 265 | 'To see help on specific argument, do:\n' |
guo yunxian | 306a7de | 2016-10-20 18:33:21 +0800 | [diff] [blame] | 266 | 'tempest account-generator <ARG> -h') |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 267 | parser = argparse.ArgumentParser( |
| 268 | description=DESCRIPTION, |
| 269 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
| 270 | usage=usage_string |
| 271 | ) |
| 272 | |
| 273 | _parser_add_args(parser) |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 274 | opts = parser.parse_args() |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 275 | return opts |
| 276 | |
| 277 | |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 278 | class 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 Igawa | 9262943 | 2016-06-09 12:28:09 +0900 | [diff] [blame] | 287 | main(parsed_args) |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 288 | except Exception: |
| 289 | LOG.exception("Failure generating test accounts.") |
| 290 | traceback.print_exc() |
| 291 | raise |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 292 | |
| 293 | def get_description(self): |
| 294 | return DESCRIPTION |
| 295 | |
| 296 | |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 297 | def main(opts=None): |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 298 | setup_logging() |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 299 | if not opts: |
BinBin Cong | c7f7feb | 2016-02-17 11:22:46 +0000 | [diff] [blame] | 300 | LOG.warning("Use of: 'tempest-account-generator' is deprecated, " |
| 301 | "please use: 'tempest account-generator'") |
David Paterson | 68b8b9d | 2015-12-01 15:44:14 -0800 | [diff] [blame] | 302 | opts = get_options() |
| 303 | if opts.config_file: |
| 304 | config.CONF.set_config_path(opts.config_file) |
Andrea Frittoli (andreaf) | bd06f98 | 2016-06-02 17:26:51 +0100 | [diff] [blame] | 305 | 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) |
sslypushenko | 0de7d05 | 2015-04-16 18:49:55 +0300 | [diff] [blame] | 315 | |
| 316 | if __name__ == "__main__": |
| 317 | main() |