Allow tempest cleanup delete resources based on prefix

A warning in command description of run cleanup first with dry-run
is added. The cleanup behavior is extended to allow users to delete
only resources if their name starts with a certain prefix.

Closes-Bug: #1945082
Change-Id: I65dfe051c891b3679538acec713e8616746c47f6
This commit is contained in:
Luigi Dino Tamagnone 2023-04-09 15:24:45 +00:00 committed by Martin Kopec
parent 01c2e2ff7e
commit 9052dfcc85
11 changed files with 277 additions and 100 deletions

View File

@ -17,6 +17,16 @@
# fail early if anything missing the IPv6 settings or deployments.
- devstack-ipv6-only-deployments-verification
tasks:
- name: Run tempest cleanup init-saved-state
include_role:
name: tempest-cleanup
vars:
init_saved_state: true
when: (run_tempest_dry_cleanup is defined and run_tempest_dry_cleanup | bool) or
(run_tempest_cleanup is defined and run_tempest_cleanup | bool) or
(run_tempest_fail_if_leaked_resources is defined and run_tempest_fail_if_leaked_resources | bool) or
(run_tempest_cleanup_prefix is defined and run_tempest_cleanup_prefix | bool)
- name: Run Tempest version <= 26.0.0
include_role:
name: run-tempest-26
@ -30,3 +40,15 @@
when:
- zuul.branch is defined
- zuul.branch not in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky", "stable/stein"]
- name: Run tempest cleanup dry-run
include_role:
name: tempest-cleanup
vars:
dry_run: true
when: run_tempest_dry_cleanup is defined and run_tempest_dry_cleanup | bool
- name: Run tempest cleanup
include_role:
name: tempest-cleanup
when: run_tempest_cleanup is defined and run_tempest_cleanup | bool

View File

@ -27,7 +27,8 @@
init_saved_state: true
when: (run_tempest_dry_cleanup is defined and run_tempest_dry_cleanup | bool) or
(run_tempest_cleanup is defined and run_tempest_cleanup | bool) or
(run_tempest_fail_if_leaked_resources is defined and run_tempest_fail_if_leaked_resources | bool)
(run_tempest_fail_if_leaked_resources is defined and run_tempest_fail_if_leaked_resources | bool) or
(run_tempest_cleanup_prefix is defined and run_tempest_cleanup_prefix | bool)
- name: Run Tempest version <= 26.0.0
include_role:

View File

@ -0,0 +1,10 @@
---
features:
- |
We add a new argument, ``--prefix``, to ``tempest cleanup`` tool that will
allow users delete only resources that match the prefix. When this option
is used, ``saved_state.json`` file is not needed (no need to run with
``--init-saved-state`` first). If there is one, it will be ignored and the
cleanup will be done based on the given prefix only.
Note, that some resources are not named thus they will not be deleted when
filtering based on the prefix.

View File

@ -40,6 +40,12 @@ saved_state.json file.
some must have been leaked. This can be also used to verify that tempest
cleanup was successful.
.. zuul:rolevar:: run_tempest_cleanup_prefix
:default: false
When true, tempest cleanup will be called with '--prefix tempest' to delete
only resources with names that match the prefix. This option can be used
together with dry_run.
Role usage
----------

View File

@ -2,3 +2,4 @@ devstack_base_dir: /opt/stack
init_saved_state: false
dry_run: false
run_tempest_fail_if_leaked_resources: false
run_tempest_cleanup_prefix: false

View File

@ -5,3 +5,12 @@
command: tox -evenv-tempest -- tempest cleanup --dry-run --debug
args:
chdir: "{{ devstack_base_dir }}/tempest"
when: not run_tempest_cleanup_prefix
- name: Run tempest cleanup dry-run with tempest prefix
become: yes
become_user: tempest
command: tox -evenv-tempest -- tempest cleanup --dry-run --debug --prefix tempest
args:
chdir: "{{ devstack_base_dir }}/tempest"
when: run_tempest_cleanup_prefix

View File

@ -27,6 +27,15 @@
command: tox -evenv-tempest -- tempest cleanup --debug
args:
chdir: "{{ devstack_base_dir }}/tempest"
when: not run_tempest_cleanup_prefix
- name: Run tempest cleanup with tempest prefix
become: yes
become_user: tempest
command: tox -evenv-tempest -- tempest cleanup --debug --prefix tempest
args:
chdir: "{{ devstack_base_dir }}/tempest"
when: run_tempest_cleanup_prefix
- when:
- run_tempest_fail_if_leaked_resources

View File

@ -26,6 +26,10 @@ specified in ``tempest.conf`` is never deleted.
Example Run
-----------
.. warning::
We advice not to run tempest cleanup on production environments.
.. warning::
If step 1 is skipped in the example below, the cleanup procedure
@ -45,7 +49,10 @@ Runtime Arguments
* ``--init-saved-state``: Initializes the saved state of the OpenStack
deployment and will output a ``saved_state.json`` file containing resources
from your deployment that will be preserved from the cleanup command. This
should be done prior to running Tempest tests.
should be done prior to running Tempest tests. Note, that if other users of
your cloud could have created resources after running ``--init-saved-state``,
it would not protect those resources as they wouldn't be present in the
saved_state.json file.
* ``--delete-tempest-conf-objects``: If option is present, then the command
will delete the admin project in addition to the resources associated with
@ -58,7 +65,27 @@ Runtime Arguments
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.
report. We STRONGLY ENCOURAGE to run ``tempest cleanup`` with ``--dry-run``
first and then verify that the resources listed in the ``dry_run.json`` file
are meant to be deleted.
* ``--prefix``: Only resources that match the prefix will be deleted. When this
option is used, ``saved_state.json`` file is not needed (no need to run with
``--init-saved-state`` first).
All tempest resources are created with the prefix value from the config
option ``resource_name_prefix`` in tempest.conf. To cleanup only the
resources created by tempest, you should use the prefix set in your
tempest.conf (the default value of ``resource_name_prefix`` is ``tempest``.
Note, that some resources are not named thus they will not be deleted when
filtering based on the prefix. This option will be ignored when
``--init-saved-state`` is used so that it can capture the true init state -
all resources present at that moment. If there is any ``saved_state.json``
file present (e.g. if you ran the tempest cleanup with ``--init-saved-state``
before) and you run the tempest cleanup with ``--prefix``, the
``saved_state.json`` file will be ignored and cleanup will be done based on
the passed prefix only.
* ``--help``: Print the help text for the command and parameters.
@ -157,6 +184,7 @@ class TempestCleanup(command.Command):
is_dry_run = self.options.dry_run
is_preserve = not self.options.delete_tempest_conf_objects
is_save_state = False
cleanup_prefix = self.options.prefix
if is_dry_run:
self.dry_run_data["_projects_to_clean"] = {}
@ -168,7 +196,8 @@ class TempestCleanup(command.Command):
'is_dry_run': is_dry_run,
'saved_state_json': self.json_data,
'is_preserve': False,
'is_save_state': is_save_state}
'is_save_state': is_save_state,
'prefix': cleanup_prefix}
project_service = cleanup_service.ProjectService(admin_mgr, **kwargs)
projects = project_service.list()
LOG.info("Processing %s projects", len(projects))
@ -182,6 +211,7 @@ class TempestCleanup(command.Command):
'saved_state_json': self.json_data,
'is_preserve': is_preserve,
'is_save_state': is_save_state,
'prefix': cleanup_prefix,
'got_exceptions': self.GOT_EXCEPTIONS}
LOG.info("Processing global services")
for service in self.global_services:
@ -206,6 +236,7 @@ class TempestCleanup(command.Command):
project_id = project['id']
project_name = project['name']
project_data = None
cleanup_prefix = self.options.prefix
if is_dry_run:
project_data = dry_run_data["_projects_to_clean"][project_id] = {}
project_data['name'] = project_name
@ -216,6 +247,7 @@ class TempestCleanup(command.Command):
'is_preserve': is_preserve,
'is_save_state': False,
'project_id': project_id,
'prefix': cleanup_prefix,
'got_exceptions': self.GOT_EXCEPTIONS}
for service in self.project_associated_services:
svc = service(self.admin_mgr, **kwargs)
@ -243,10 +275,26 @@ class TempestCleanup(command.Command):
help="Generate JSON file:" + DRY_RUN_JSON +
", that reports the objects that would have "
"been deleted had a full cleanup been run.")
parser.add_argument('--prefix', dest='prefix', default=None,
help="Only resources that match the prefix will "
"be deleted (resources in saved_state.json are "
"not taken into account). All tempest resources "
"are created with the prefix value set by "
"resource_name_prefix in tempest.conf, default "
"prefix is tempest. Note that some resources are "
"not named thus they will not be deleted when "
"filtering based on the prefix. This opt will be "
"ignored when --init-saved-state is used so that "
"it can capture the true init state - all "
"resources present at that moment.")
return parser
def get_description(self):
return 'Cleanup after tempest run'
return ('tempest cleanup tool, read the full documentation before '
'using this tool. We advice not to run it on production '
'environments. On environments where also other users may '
'create resources, we strongly advice using --dry-run '
'argument first and verify the content of dry_run.json file.')
def _init_state(self):
LOG.info("Initializing saved state.")
@ -257,6 +305,10 @@ class TempestCleanup(command.Command):
'saved_state_json': data,
'is_preserve': False,
'is_save_state': True,
# must be None as we want to capture true init state
# (all resources present) thus no filtering based
# on the prefix
'prefix': None,
'got_exceptions': self.GOT_EXCEPTIONS}
for service in self.global_services:
svc = service(admin_mgr, **kwargs)

View File

@ -115,6 +115,16 @@ class BaseService(object):
return [item for item in item_list
if item['tenant_id'] == self.tenant_id]
def _filter_by_prefix(self, item_list):
items = [item for item in item_list
if item['name'].startswith(self.prefix)]
return items
def _filter_out_ids_from_saved(self, item_list, attr):
items = [item for item in item_list if item['id']
not in self.saved_state_json[attr].keys()]
return items
def list(self):
pass
@ -156,10 +166,11 @@ class SnapshotService(BaseService):
def list(self):
client = self.client
snaps = client.list_snapshots()['snapshots']
if not self.is_save_state:
if self.prefix:
snaps = self._filter_by_prefix(snaps)
elif not self.is_save_state:
# recreate list removing saved snapshots
snaps = [snap for snap in snaps if snap['id']
not in self.saved_state_json['snapshots'].keys()]
snaps = self._filter_out_ids_from_saved(snaps, 'snapshots')
LOG.debug("List count, %s Snapshots", len(snaps))
return snaps
@ -194,10 +205,11 @@ class ServerService(BaseService):
client = self.client
servers_body = client.list_servers()
servers = servers_body['servers']
if not self.is_save_state:
if self.prefix:
servers = self._filter_by_prefix(servers)
elif not self.is_save_state:
# recreate list removing saved servers
servers = [server for server in servers if server['id']
not in self.saved_state_json['servers'].keys()]
servers = self._filter_out_ids_from_saved(servers, 'servers')
LOG.debug("List count, %s Servers", len(servers))
return servers
@ -227,10 +239,11 @@ class ServerGroupService(ServerService):
def list(self):
client = self.server_groups_client
sgs = client.list_server_groups()['server_groups']
if not self.is_save_state:
if self.prefix:
sgs = self._filter_by_prefix(sgs)
elif not self.is_save_state:
# recreate list removing saved server_groups
sgs = [sg for sg in sgs if sg['id']
not in self.saved_state_json['server_groups'].keys()]
sgs = self._filter_out_ids_from_saved(sgs, 'server_groups')
LOG.debug("List count, %s Server Groups", len(sgs))
return sgs
@ -263,7 +276,9 @@ class KeyPairService(BaseService):
def list(self):
client = self.client
keypairs = client.list_keypairs()['keypairs']
if not self.is_save_state:
if self.prefix:
keypairs = self._filter_by_prefix(keypairs)
elif not self.is_save_state:
# recreate list removing saved keypairs
keypairs = [keypair for keypair in keypairs
if keypair['keypair']['name']
@ -302,10 +317,11 @@ class VolumeService(BaseService):
def list(self):
client = self.client
vols = client.list_volumes()['volumes']
if not self.is_save_state:
if self.prefix:
vols = self._filter_by_prefix(vols)
elif not self.is_save_state:
# recreate list removing saved volumes
vols = [vol for vol in vols if vol['id']
not in self.saved_state_json['volumes'].keys()]
vols = self._filter_out_ids_from_saved(vols, 'volumes')
LOG.debug("List count, %s Volumes", len(vols))
return vols
@ -336,6 +352,10 @@ class VolumeQuotaService(BaseService):
self.client = manager.volume_quotas_client_latest
def delete(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore do nothing
return
client = self.client
try:
LOG.debug("Deleting Volume Quotas for project with id %s",
@ -346,6 +366,10 @@ class VolumeQuotaService(BaseService):
self.project_id)
def dry_run(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore do nothing
return
quotas = self.client.show_quota_set(
self.project_id, params={'usage': True})['quota_set']
self.data['volume_quotas'] = quotas
@ -358,6 +382,10 @@ class NovaQuotaService(BaseService):
self.limits_client = manager.limits_client
def delete(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore do nothing
return
client = self.client
try:
LOG.debug("Deleting Nova Quotas for project with id %s",
@ -368,6 +396,10 @@ class NovaQuotaService(BaseService):
self.project_id)
def dry_run(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore do nothing
return
client = self.limits_client
quotas = client.show_limits()['limits']
self.data['compute_quotas'] = quotas['absolute']
@ -379,6 +411,10 @@ class NetworkQuotaService(BaseService):
self.client = manager.network_quotas_client
def delete(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore do nothing
return
client = self.client
try:
LOG.debug("Deleting Network Quotas for project with id %s",
@ -389,6 +425,10 @@ class NetworkQuotaService(BaseService):
self.project_id)
def dry_run(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore do nothing
return
resp = [quota for quota in self.client.list_quotas()['quotas']
if quota['project_id'] == self.project_id]
self.data['network_quotas'] = resp
@ -422,11 +462,13 @@ class NetworkService(BaseNetworkService):
client = self.networks_client
networks = client.list_networks(**self.tenant_filter)
networks = networks['networks']
if not self.is_save_state:
# recreate list removing saved networks
networks = [network for network in networks if network['id']
not in self.saved_state_json['networks'].keys()]
if self.prefix:
networks = self._filter_by_prefix(networks)
else:
if not self.is_save_state:
# recreate list removing saved networks
networks = self._filter_out_ids_from_saved(
networks, 'networks')
# filter out networks declared in tempest.conf
if self.is_preserve:
networks = [network for network in networks
@ -458,14 +500,17 @@ class NetworkService(BaseNetworkService):
class NetworkFloatingIpService(BaseNetworkService):
def list(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore return empty list
return []
client = self.floating_ips_client
flips = client.list_floatingips(**self.tenant_filter)
flips = flips['floatingips']
if not self.is_save_state:
# recreate list removing saved flips
flips = [flip for flip in flips if flip['id']
not in self.saved_state_json['floatingips'].keys()]
flips = self._filter_out_ids_from_saved(flips, 'floatingips')
LOG.debug("List count, %s Network Floating IPs", len(flips))
return flips
@ -498,15 +543,15 @@ class NetworkRouterService(BaseNetworkService):
client = self.routers_client
routers = client.list_routers(**self.tenant_filter)
routers = routers['routers']
if not self.is_save_state:
# recreate list removing saved routers
routers = [router for router in routers if router['id']
not in self.saved_state_json['routers'].keys()]
if self.prefix:
routers = self._filter_by_prefix(routers)
else:
if not self.is_save_state:
# recreate list removing saved routers
routers = self._filter_out_ids_from_saved(routers, 'routers')
if self.is_preserve:
routers = [router for router in routers
if router['id'] != CONF_PUB_ROUTER]
LOG.debug("List count, %s Routers", len(routers))
return routers
@ -547,15 +592,19 @@ class NetworkRouterService(BaseNetworkService):
class NetworkMeteringLabelRuleService(NetworkService):
def list(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore return empty list
return []
client = self.metering_label_rules_client
rules = client.list_metering_label_rules()
rules = rules['metering_label_rules']
rules = self._filter_by_tenant_id(rules)
if not self.is_save_state:
saved_rules = self.saved_state_json['metering_label_rules'].keys()
rules = self._filter_out_ids_from_saved(
rules, 'metering_label_rules')
# recreate list removing saved rules
rules = [rule for rule in rules if rule['id'] not in saved_rules]
LOG.debug("List count, %s Metering Label Rules", len(rules))
return rules
@ -589,11 +638,12 @@ class NetworkMeteringLabelService(BaseNetworkService):
labels = client.list_metering_labels()
labels = labels['metering_labels']
labels = self._filter_by_tenant_id(labels)
if not self.is_save_state:
if self.prefix:
labels = self._filter_by_prefix(labels)
elif not self.is_save_state:
# recreate list removing saved labels
labels = [label for label in labels if label['id']
not in self.saved_state_json['metering_labels'].keys()]
labels = self._filter_out_ids_from_saved(
labels, 'metering_labels')
LOG.debug("List count, %s Metering Labels", len(labels))
return labels
@ -627,14 +677,14 @@ class NetworkPortService(BaseNetworkService):
client.list_ports(**self.tenant_filter)['ports']
if port["device_owner"] == "" or
port["device_owner"].startswith("compute:")]
if not self.is_save_state:
# recreate list removing saved ports
ports = [port for port in ports if port['id']
not in self.saved_state_json['ports'].keys()]
if self.prefix:
ports = self._filter_by_prefix(ports)
else:
if not self.is_save_state:
# recreate list removing saved ports
ports = self._filter_out_ids_from_saved(ports, 'ports')
if self.is_preserve:
ports = self._filter_by_conf_networks(ports)
LOG.debug("List count, %s Ports", len(ports))
return ports
@ -667,16 +717,18 @@ class NetworkSecGroupService(BaseNetworkService):
secgroups = [secgroup for secgroup in
client.list_security_groups(**filter)['security_groups']
if secgroup['name'] != 'default']
if not self.is_save_state:
# recreate list removing saved security_groups
secgroups = [secgroup for secgroup in secgroups if secgroup['id']
not in self.saved_state_json['security_groups'].keys()
]
if self.prefix:
secgroups = self._filter_by_prefix(secgroups)
else:
if not self.is_save_state:
# recreate list removing saved security_groups
secgroups = self._filter_out_ids_from_saved(
secgroups, 'security_groups')
if self.is_preserve:
secgroups = [secgroup for secgroup in secgroups
if secgroup['security_group_rules'][0]['project_id']
not in CONF_PROJECTS]
secgroups = [
secgroup for secgroup in secgroups
if secgroup['security_group_rules'][0]['project_id']
not in CONF_PROJECTS]
LOG.debug("List count, %s security_groups", len(secgroups))
return secgroups
@ -708,10 +760,12 @@ class NetworkSubnetService(BaseNetworkService):
client = self.subnets_client
subnets = client.list_subnets(**self.tenant_filter)
subnets = subnets['subnets']
if not self.is_save_state:
# recreate list removing saved subnets
subnets = [subnet for subnet in subnets if subnet['id']
not in self.saved_state_json['subnets'].keys()]
if self.prefix:
subnets = self._filter_by_prefix(subnets)
else:
if not self.is_save_state:
# recreate list removing saved subnets
subnets = self._filter_out_ids_from_saved(subnets, 'subnets')
if self.is_preserve:
subnets = self._filter_by_conf_networks(subnets)
LOG.debug("List count, %s Subnets", len(subnets))
@ -743,10 +797,12 @@ class NetworkSubnetPoolsService(BaseNetworkService):
def list(self):
client = self.subnetpools_client
pools = client.list_subnetpools(**self.tenant_filter)['subnetpools']
if not self.is_save_state:
# recreate list removing saved subnet pools
pools = [pool for pool in pools if pool['id']
not in self.saved_state_json['subnetpools'].keys()]
if self.prefix:
pools = self._filter_by_prefix(pools)
else:
if not self.is_save_state:
# recreate list removing saved subnet pools
pools = self._filter_out_ids_from_saved(pools, 'subnetpools')
if self.is_preserve:
pools = [pool for pool in pools if pool['project_id']
not in CONF_PROJECTS]
@ -782,11 +838,15 @@ class RegionService(BaseService):
self.client = manager.regions_client
def list(self):
if self.prefix:
# this means we're cleaning resources based on a certain prefix,
# this resource doesn't have a name, therefore return empty list
return []
client = self.client
regions = client.list_regions()
if not self.is_save_state:
regions = [region for region in regions['regions'] if region['id']
not in self.saved_state_json['regions'].keys()]
regions = self._filter_out_ids_from_saved(
regions['regions'], 'regions')
LOG.debug("List count, %s Regions", len(regions))
return regions
else:
@ -824,11 +884,12 @@ class FlavorService(BaseService):
def list(self):
client = self.client
flavors = client.list_flavors({"is_public": None})['flavors']
if not self.is_save_state:
# recreate list removing saved flavors
flavors = [flavor for flavor in flavors if flavor['id']
not in self.saved_state_json['flavors'].keys()]
if self.prefix:
flavors = self._filter_by_prefix(flavors)
else:
if not self.is_save_state:
# recreate list removing saved flavors
flavors = self._filter_out_ids_from_saved(flavors, 'flavors')
if self.is_preserve:
flavors = [flavor for flavor in flavors
if flavor['id'] not in CONF_FLAVORS]
@ -871,10 +932,11 @@ class ImageService(BaseService):
marker = urllib.parse_qs(parsed.query)['marker'][0]
response = client.list_images(params={"marker": marker})
images.extend(response['images'])
if not self.is_save_state:
images = [image for image in images if image['id']
not in self.saved_state_json['images'].keys()]
if self.prefix:
images = self._filter_by_prefix(images)
else:
if not self.is_save_state:
images = self._filter_out_ids_from_saved(images, 'images')
if self.is_preserve:
images = [image for image in images
if image['id'] not in CONF_IMAGES]
@ -910,19 +972,17 @@ class UserService(BaseService):
def list(self):
users = self.client.list_users()['users']
if not self.is_save_state:
users = [user for user in users if user['id']
not in self.saved_state_json['users'].keys()]
if self.prefix:
users = self._filter_by_prefix(users)
else:
if not self.is_save_state:
users = self._filter_out_ids_from_saved(users, 'users')
if self.is_preserve:
users = [user for user in users if user['name']
not in CONF_USERS]
elif not self.is_save_state: # Never delete admin user
users = [user for user in users if user['name'] !=
CONF.auth.admin_username]
LOG.debug("List count, %s Users after reconcile", len(users))
return users
@ -955,13 +1015,14 @@ class RoleService(BaseService):
def list(self):
try:
roles = self.client.list_roles()['roles']
# reconcile roles with saved state and never list admin role
if not self.is_save_state:
roles = [role for role in roles if
(role['id'] not in
self.saved_state_json['roles'].keys() and
role['name'] != CONF.identity.admin_role)]
LOG.debug("List count, %s Roles after reconcile", len(roles))
if self.prefix:
roles = self._filter_by_prefix(roles)
elif not self.is_save_state:
# reconcile roles with saved state and never list admin role
roles = self._filter_out_ids_from_saved(roles, 'roles')
roles = [role for role in roles
if role['name'] != CONF.identity.admin_role]
LOG.debug("List count, %s Roles after reconcile", len(roles))
return roles
except Exception:
LOG.exception("Cannot retrieve Roles.")
@ -995,18 +1056,17 @@ class ProjectService(BaseService):
def list(self):
projects = self.client.list_projects()['projects']
if not self.is_save_state:
project_ids = self.saved_state_json['projects']
projects = [project
for project in projects
if (project['id'] not in project_ids and
project['name'] != CONF.auth.admin_project_name)]
if self.prefix:
projects = self._filter_by_prefix(projects)
else:
if not self.is_save_state:
projects = self._filter_out_ids_from_saved(
projects, 'projects')
projects = [project for project in projects
if project['name'] != CONF.auth.admin_project_name]
if self.is_preserve:
projects = [project
for project in projects
projects = [project for project in projects
if project['name'] not in CONF_PROJECTS]
LOG.debug("List count, %s Projects after reconcile", len(projects))
return projects
@ -1039,10 +1099,10 @@ class DomainService(BaseService):
def list(self):
client = self.client
domains = client.list_domains()['domains']
if not self.is_save_state:
domains = [domain for domain in domains if domain['id']
not in self.saved_state_json['domains'].keys()]
if self.prefix:
domains = self._filter_by_prefix(domains)
elif not self.is_save_state:
domains = self._filter_out_ids_from_saved(domains, 'domains')
LOG.debug("List count, %s Domains after reconcile", len(domains))
return domains

View File

@ -44,6 +44,7 @@ class TestBaseService(base.TestCase):
'saved_state_json': {'saved': 'data'},
'is_preserve': False,
'is_save_state': True,
'prefix': 'tempest',
'tenant_id': 'project_id',
'got_exceptions': []}
base = cleanup_service.BaseService(kwargs)
@ -54,6 +55,7 @@ class TestBaseService(base.TestCase):
self.assertTrue(base.is_save_state)
self.assertEqual(base.tenant_filter['project_id'], kwargs['tenant_id'])
self.assertEqual(base.got_exceptions, kwargs['got_exceptions'])
self.assertEqual(base.prefix, kwargs['prefix'])
def test_not_implemented_ex(self):
kwargs = {'data': {'data': 'test'},
@ -61,6 +63,7 @@ class TestBaseService(base.TestCase):
'saved_state_json': {'saved': 'data'},
'is_preserve': False,
'is_save_state': False,
'prefix': 'tempest',
'tenant_id': 'project_id',
'got_exceptions': []}
base = self.TestException(kwargs)
@ -188,7 +191,8 @@ class BaseCmdServiceTests(MockFunctionsBase):
service_name = 'default'
def _create_cmd_service(self, service_type, is_save_state=False,
is_preserve=False, is_dry_run=False):
is_preserve=False, is_dry_run=False,
prefix=''):
creds = fake_credentials.FakeKeystoneV3Credentials()
os = clients.Manager(creds)
return getattr(cleanup_service, service_type)(
@ -196,6 +200,7 @@ class BaseCmdServiceTests(MockFunctionsBase):
is_save_state=is_save_state,
is_preserve=is_preserve,
is_dry_run=is_dry_run,
prefix=prefix,
project_id='b8e3ece07bb049138d224436756e3b57',
data={},
saved_state_json=self.saved_state

View File

@ -58,6 +58,8 @@
Base integration test with Neutron networking, IPv6 and py3.
vars:
tox_envlist: full
run_tempest_cleanup: true
run_tempest_cleanup_prefix: true
devstack_localrc:
USE_PYTHON3: true
FORCE_CONFIG_DRIVE: true