Merge "Remove v2 identity from tempest cleanup command"

This commit is contained in:
Zuul 2017-11-16 03:21:58 +00:00 committed by Gerrit Code Review
commit 7c8dd48f75
3 changed files with 131 additions and 108 deletions

View File

@ -54,17 +54,17 @@ associated with the Tempest and alternate Tempest users and projects but will
not delete the projects themselves.
**--dry-run**: Creates a report (``./dry_run.json``) of the projects that will
be cleaned up (in the ``_tenants_to_clean`` dictionary [1]_) and the global
be cleaned up (in the ``_projects_to_clean`` dictionary [1]_) and the global
objects that will be removed (domains, flavors, images, roles, projects,
and users). Once the cleanup command is executed (e.g. run without
parameters), running it again with **--dry-run** should yield an empty report.
**--help**: Print the help text for the command and parameters.
.. [1] The ``_tenants_to_clean`` dictionary in ``dry_run.json`` lists the
.. [1] The ``_projects_to_clean`` dictionary in ``dry_run.json`` lists the
projects that ``tempest cleanup`` will loop through to delete child
objects, but the command will, by default, not delete the projects
themselves. This may differ from the ``tenants`` list as you can clean
themselves. This may differ from the ``projects`` list as you can clean
the Tempest and alternate Tempest users and projects but they will not be
deleted unless the **--delete-tempest-conf-objects** flag is used to
force their deletion.
@ -111,13 +111,13 @@ class TempestCleanup(command.Command):
self.admin_id = ""
self.admin_role_id = ""
self.admin_tenant_id = ""
self.admin_project_id = ""
self._init_admin_ids()
self.admin_role_added = []
# available services
self.tenant_services = cleanup_service.get_tenant_cleanup_services()
self.project_services = cleanup_service.get_project_cleanup_services()
self.global_services = cleanup_service.get_global_cleanup_services()
if parsed_args.init_saved_state:
@ -133,24 +133,24 @@ class TempestCleanup(command.Command):
is_save_state = False
if is_dry_run:
self.dry_run_data["_tenants_to_clean"] = {}
self.dry_run_data["_projects_to_clean"] = {}
admin_mgr = self.admin_mgr
# Always cleanup tempest and alt tempest tenants unless
# Always cleanup tempest and alt tempest projects unless
# they are in saved state json. Therefore is_preserve is False
kwargs = {'data': self.dry_run_data,
'is_dry_run': is_dry_run,
'saved_state_json': self.json_data,
'is_preserve': False,
'is_save_state': is_save_state}
tenant_service = cleanup_service.TenantService(admin_mgr, **kwargs)
tenants = tenant_service.list()
print("Process %s tenants" % len(tenants))
project_service = cleanup_service.ProjectService(admin_mgr, **kwargs)
projects = project_service.list()
print("Process %s projects" % len(projects))
# Loop through list of tenants and clean them up.
for tenant in tenants:
self._add_admin(tenant['id'])
self._clean_tenant(tenant)
# Loop through list of projects and clean them up.
for project in projects:
self._add_admin(project['id'])
self._clean_project(project)
kwargs = {'data': self.dry_run_data,
'is_dry_run': is_dry_run,
@ -169,49 +169,51 @@ class TempestCleanup(command.Command):
self._remove_admin_user_roles()
def _remove_admin_user_roles(self):
tenant_ids = self.admin_role_added
LOG.debug("Removing admin user roles where needed for tenants: %s",
tenant_ids)
for tenant_id in tenant_ids:
self._remove_admin_role(tenant_id)
project_ids = self.admin_role_added
LOG.debug("Removing admin user roles where needed for projects: %s",
project_ids)
for project_id in project_ids:
self._remove_admin_role(project_id)
def _clean_tenant(self, tenant):
print("Cleaning tenant: %s " % tenant['name'])
def _clean_project(self, project):
print("Cleaning project: %s " % project['name'])
is_dry_run = self.options.dry_run
dry_run_data = self.dry_run_data
is_preserve = not self.options.delete_tempest_conf_objects
tenant_id = tenant['id']
tenant_name = tenant['name']
tenant_data = None
project_id = project['id']
project_name = project['name']
project_data = None
if is_dry_run:
tenant_data = dry_run_data["_tenants_to_clean"][tenant_id] = {}
tenant_data['name'] = tenant_name
project_data = dry_run_data["_projects_to_clean"][project_id] = {}
project_data['name'] = project_name
kwargs = {"username": CONF.auth.admin_username,
"password": CONF.auth.admin_password,
"tenant_name": tenant['name']}
"project_name": project['name']}
mgr = clients.Manager(credentials=credentials.get_credentials(
**kwargs))
kwargs = {'data': tenant_data,
kwargs = {'data': project_data,
'is_dry_run': is_dry_run,
'saved_state_json': None,
'is_preserve': is_preserve,
'is_save_state': False,
'tenant_id': tenant_id}
for service in self.tenant_services:
'project_id': project_id}
for service in self.project_services:
svc = service(mgr, **kwargs)
svc.run()
def _init_admin_ids(self):
tn_cl = self.admin_mgr.tenants_client
rl_cl = self.admin_mgr.roles_client
pr_cl = self.admin_mgr.projects_client
rl_cl = self.admin_mgr.roles_v3_client
rla_cl = self.admin_mgr.role_assignments_client
us_cl = self.admin_mgr.users_v3_client
tenant = identity.get_tenant_by_name(tn_cl,
CONF.auth.admin_project_name)
self.admin_tenant_id = tenant['id']
user = identity.get_user_by_username(tn_cl, self.admin_tenant_id,
CONF.auth.admin_username)
project = identity.get_project_by_name(pr_cl,
CONF.auth.admin_project_name)
self.admin_project_id = project['id']
user = identity.get_user_by_project(us_cl, rla_cl,
self.admin_project_id,
CONF.auth.admin_username)
self.admin_id = user['id']
roles = rl_cl.list_roles()['roles']
@ -236,7 +238,7 @@ class TempestCleanup(command.Command):
dest='delete_tempest_conf_objects',
default=False,
help="Force deletion of the tempest and "
"alternate tempest users and tenants.")
"alternate tempest users and projects.")
parser.add_argument('--dry-run', action="store_true",
dest='dry_run', default=False,
help="Generate JSON file:" + DRY_RUN_JSON +
@ -247,44 +249,44 @@ class TempestCleanup(command.Command):
def get_description(self):
return 'Cleanup after tempest run'
def _add_admin(self, tenant_id):
rl_cl = self.admin_mgr.roles_client
def _add_admin(self, project_id):
rl_cl = self.admin_mgr.roles_v3_client
needs_role = True
roles = rl_cl.list_user_roles_on_project(tenant_id,
roles = rl_cl.list_user_roles_on_project(project_id,
self.admin_id)['roles']
for role in roles:
if role['id'] == self.admin_role_id:
needs_role = False
LOG.debug("User already had admin privilege for this tenant")
LOG.debug("User already had admin privilege for this project")
if needs_role:
LOG.debug("Adding admin privilege for : %s", tenant_id)
rl_cl.create_user_role_on_project(tenant_id, self.admin_id,
LOG.debug("Adding admin privilege for : %s", project_id)
rl_cl.create_user_role_on_project(project_id, self.admin_id,
self.admin_role_id)
self.admin_role_added.append(tenant_id)
self.admin_role_added.append(project_id)
def _remove_admin_role(self, tenant_id):
LOG.debug("Remove admin user role for tenant: %s", tenant_id)
def _remove_admin_role(self, project_id):
LOG.debug("Remove admin user role for projectt: %s", project_id)
# Must initialize Admin Manager for each user role
# Otherwise authentication exception is thrown, weird
id_cl = clients.Manager(
credentials.get_configured_admin_credentials()).identity_client
if (self._tenant_exists(tenant_id)):
if (self._project_exists(project_id)):
try:
id_cl.delete_role_from_user_on_project(tenant_id,
id_cl.delete_role_from_user_on_project(project_id,
self.admin_id,
self.admin_role_id)
except Exception as ex:
LOG.exception("Failed removing role from tenant which still"
LOG.exception("Failed removing role from project which still"
"exists, exception: %s", ex)
def _tenant_exists(self, tenant_id):
tn_cl = self.admin_mgr.tenants_client
def _project_exists(self, project_id):
pr_cl = self.admin_mgr.projects_client
try:
t = tn_cl.show_tenant(tenant_id)
LOG.debug("Tenant is: %s", str(t))
p = pr_cl.show_project(project_id)
LOG.debug("Project is: %s", str(p))
return True
except Exception as ex:
LOG.debug("Tenant no longer exists? %s", ex)
LOG.debug("Project no longer exists? %s", ex)
return False
def _init_state(self):

View File

@ -32,7 +32,7 @@ CONF_NETWORKS = []
CONF_PRIV_NETWORK_NAME = None
CONF_PUB_NETWORK = None
CONF_PUB_ROUTER = None
CONF_TENANTS = None
CONF_PROJECTS = None
CONF_USERS = None
IS_CINDER = None
@ -50,7 +50,7 @@ def init_conf():
global CONF_PRIV_NETWORK_NAME
global CONF_PUB_NETWORK
global CONF_PUB_ROUTER
global CONF_TENANTS
global CONF_PROJECTS
global CONF_USERS
global IS_CINDER
global IS_GLANCE
@ -69,7 +69,7 @@ def init_conf():
CONF_PRIV_NETWORK_NAME = CONF.compute.fixed_network_name
CONF_PUB_NETWORK = CONF.network.public_network_id
CONF_PUB_ROUTER = CONF.network.public_router_id
CONF_TENANTS = [CONF.auth.admin_project_name]
CONF_PROJECTS = [CONF.auth.admin_project_name]
CONF_USERS = [CONF.auth.admin_username]
if IS_NEUTRON:
@ -82,14 +82,14 @@ def _get_network_id(net_name, project_name):
am = clients.Manager(
credentials.get_configured_admin_credentials())
net_cl = am.networks_client
tn_cl = am.tenants_client
pr_cl = am.projects_client
networks = net_cl.list_networks()
tenant = identity.get_tenant_by_name(tn_cl, project_name)
t_id = tenant['id']
project = identity.get_project_by_name(pr_cl, project_name)
p_id = project['id']
n_id = None
for net in networks['networks']:
if (net['tenant_id'] == t_id and net['name'] == net_name):
if (net['project_id'] == p_id and net['name'] == net_name):
n_id = net['id']
break
return n_id
@ -786,14 +786,14 @@ class ImageService(BaseService):
class IdentityService(BaseService):
def __init__(self, manager, **kwargs):
super(IdentityService, self).__init__(kwargs)
self.client = manager.identity_client
self.client = manager.identity_v3_client
class UserService(BaseService):
def __init__(self, manager, **kwargs):
super(UserService, self).__init__(kwargs)
self.client = manager.users_client
self.client = manager.users_v3_client
def list(self):
users = self.client.list_users()['users']
@ -872,43 +872,43 @@ class RoleService(BaseService):
self.data['roles'][role['id']] = role['name']
class TenantService(BaseService):
class ProjectService(BaseService):
def __init__(self, manager, **kwargs):
super(TenantService, self).__init__(kwargs)
self.client = manager.tenants_client
super(ProjectService, self).__init__(kwargs)
self.client = manager.projects_client
def list(self):
tenants = self.client.list_tenants()['tenants']
projects = self.client.list_projects()['projects']
if not self.is_save_state:
tenants = [tenant for tenant in tenants if (tenant['id']
not in self.saved_state_json['tenants'].keys()
and tenant['name'] != CONF.auth.admin_project_name)]
projects = [project for project in projects if (project['id']
not in self.saved_state_json['projects'].keys()
and project['name'] != CONF.auth.admin_project_name)]
if self.is_preserve:
tenants = [tenant for tenant in tenants if tenant['name']
not in CONF_TENANTS]
projects = [project for project in projects if project['name']
not in CONF_PROJECTS]
LOG.debug("List count, %s Tenants after reconcile", len(tenants))
return tenants
LOG.debug("List count, %s Projects after reconcile", len(projects))
return projects
def delete(self):
tenants = self.list()
for tenant in tenants:
projects = self.list()
for project in projects:
try:
self.client.delete_tenant(tenant['id'])
self.client.delete_project(project['id'])
except Exception:
LOG.exception("Delete Tenant exception.")
LOG.exception("Delete project exception.")
def dry_run(self):
tenants = self.list()
self.data['tenants'] = tenants
projects = self.list()
self.data['projects'] = projects
def save_state(self):
tenants = self.list()
self.data['tenants'] = {}
for tenant in tenants:
self.data['tenants'][tenant['id']] = tenant['name']
projects = self.list()
self.data['projects'] = {}
for project in projects:
self.data['projects'][project['id']] = project['name']
class DomainService(BaseService):
@ -948,35 +948,35 @@ class DomainService(BaseService):
self.data['domains'][domain['id']] = domain['name']
def get_tenant_cleanup_services():
tenant_services = []
def get_project_cleanup_services():
project_services = []
# TODO(gmann): Tempest should provide some plugin hook for cleanup
# script extension to plugin tests also.
if IS_NOVA:
tenant_services.append(ServerService)
tenant_services.append(KeyPairService)
tenant_services.append(SecurityGroupService)
tenant_services.append(ServerGroupService)
project_services.append(ServerService)
project_services.append(KeyPairService)
project_services.append(SecurityGroupService)
project_services.append(ServerGroupService)
if not IS_NEUTRON:
tenant_services.append(FloatingIpService)
tenant_services.append(NovaQuotaService)
project_services.append(FloatingIpService)
project_services.append(NovaQuotaService)
if IS_HEAT:
tenant_services.append(StackService)
project_services.append(StackService)
if IS_NEUTRON:
tenant_services.append(NetworkFloatingIpService)
project_services.append(NetworkFloatingIpService)
if utils.is_extension_enabled('metering', 'network'):
tenant_services.append(NetworkMeteringLabelRuleService)
tenant_services.append(NetworkMeteringLabelService)
tenant_services.append(NetworkRouterService)
tenant_services.append(NetworkPortService)
tenant_services.append(NetworkSubnetService)
tenant_services.append(NetworkService)
tenant_services.append(NetworkSecGroupService)
project_services.append(NetworkMeteringLabelRuleService)
project_services.append(NetworkMeteringLabelService)
project_services.append(NetworkRouterService)
project_services.append(NetworkPortService)
project_services.append(NetworkSubnetService)
project_services.append(NetworkService)
project_services.append(NetworkSecGroupService)
if IS_CINDER:
tenant_services.append(SnapshotService)
tenant_services.append(VolumeService)
tenant_services.append(VolumeQuotaService)
return tenant_services
project_services.append(SnapshotService)
project_services.append(VolumeService)
project_services.append(VolumeQuotaService)
return project_services
def get_global_cleanup_services():
@ -986,7 +986,7 @@ def get_global_cleanup_services():
if IS_GLANCE:
global_services.append(ImageService)
global_services.append(UserService)
global_services.append(TenantService)
global_services.append(ProjectService)
global_services.append(DomainService)
global_services.append(RoleService)
return global_services

View File

@ -20,6 +20,15 @@ from tempest.lib import exceptions as lib_exc
CONF = config.CONF
def get_project_by_name(client, project_name):
projects = client.list_projects({'name': project_name})['projects']
for project in projects:
if project['name'] == project_name:
return project
raise lib_exc.NotFound('No such project(%s) in %s' % (project_name,
projects))
def get_tenant_by_name(client, tenant_name):
tenants = client.list_tenants()['tenants']
for tenant in tenants:
@ -36,6 +45,18 @@ def get_user_by_username(client, tenant_id, username):
raise lib_exc.NotFound('No such user(%s) in %s' % (username, users))
def get_user_by_project(users_client, roles_client, project_id, username):
users = users_client.list_users(**{'name': username})['users']
users_in_project = roles_client.list_role_assignments(
**{'scope.project.id': project_id})['role_assignments']
for user in users:
if user['name'] == username:
for u in users_in_project:
if u['user']['id'] == user['id']:
return user
raise lib_exc.NotFound('No such user(%s) in %s' % (username, users))
def identity_utils(clients):
"""A client that abstracts v2 and v3 identity operations.