Merge "Implement purge list for tempest cleanup"
This commit is contained in:
commit
e838ec9fa8
11
releasenotes/notes/resource-list-cbf9779e8b434654.yaml
Normal file
11
releasenotes/notes/resource-list-cbf9779e8b434654.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
A new interface ``--resource-list`` has been introduced in the
|
||||
``tempest cleanup`` command to remove the resources created by
|
||||
Tempest. A new config option in the default section, ``record_resources``,
|
||||
is added to allow the recording of all resources created by Tempest.
|
||||
A list of these resources will be saved in ``resource_list.json`` file,
|
||||
which will be appended in case of multiple Tempest runs. This file
|
||||
is intended to be used with the ``tempest cleanup`` command if it is
|
||||
used with the newly added option ``--resource-list``.
|
@ -13,8 +13,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
|
||||
from tempest import config
|
||||
from tempest.lib import auth
|
||||
from tempest.lib.common.rest_client import RestClient
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
from tempest.lib.services import clients
|
||||
|
||||
@ -35,6 +40,11 @@ class Manager(clients.ServiceClients):
|
||||
super(Manager, self).__init__(
|
||||
credentials=credentials, identity_uri=identity_uri, scope=scope,
|
||||
region=CONF.identity.region)
|
||||
if CONF.record_resources:
|
||||
RestClient.lock_dir = os.path.join(
|
||||
lockutils.get_lock_path(CONF),
|
||||
'tempest-rec-rw-lock')
|
||||
RestClient.record_resources = True
|
||||
# TODO(andreaf) When clients are initialised without the right
|
||||
# parameters available, the calls below will trigger a KeyError.
|
||||
# We should catch that and raise a better error.
|
||||
|
@ -87,6 +87,23 @@ Runtime Arguments
|
||||
``saved_state.json`` file will be ignored and cleanup will be done based on
|
||||
the passed prefix only.
|
||||
|
||||
* ``--resource-list``: Allows the use of file ``./resource_list.json``, which
|
||||
contains all resources created by Tempest during all Tempest runs, to
|
||||
create another method for removing only resources created by Tempest.
|
||||
List of these resources is created when config option ``record_resources``
|
||||
in default section is set to true. After using this option for cleanup,
|
||||
the existing ``./resource_list.json`` is cleared from deleted resources.
|
||||
|
||||
When this option is used, ``saved_state.json`` file is not needed (no
|
||||
need to run with ``--init-saved-state`` first). If there is any
|
||||
``saved_state.json`` file present and you run the tempest cleanup with
|
||||
``--resource-list``, the ``saved_state.json`` file will be ignored and
|
||||
cleanup will be done based on the ``resource_list.json`` only.
|
||||
|
||||
If you run tempest cleanup with both ``--prefix`` and ``--resource-list``,
|
||||
the ``--resource-list`` option will be ignored and cleanup will be done
|
||||
based on the ``--prefix`` option only.
|
||||
|
||||
* ``--help``: Print the help text for the command and parameters.
|
||||
|
||||
.. [1] The ``_projects_to_clean`` dictionary in ``dry_run.json`` lists the
|
||||
@ -122,6 +139,7 @@ from tempest.lib import exceptions
|
||||
|
||||
SAVED_STATE_JSON = "saved_state.json"
|
||||
DRY_RUN_JSON = "dry_run.json"
|
||||
RESOURCE_LIST_JSON = "resource_list.json"
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = config.CONF
|
||||
|
||||
@ -164,6 +182,7 @@ class TempestCleanup(command.Command):
|
||||
self.admin_mgr = clients.Manager(
|
||||
credentials.get_configured_admin_credentials())
|
||||
self.dry_run_data = {}
|
||||
self.resource_data = {}
|
||||
self.json_data = {}
|
||||
|
||||
# available services
|
||||
@ -177,12 +196,20 @@ class TempestCleanup(command.Command):
|
||||
self._init_state()
|
||||
return
|
||||
|
||||
self._load_json()
|
||||
if parsed_args.prefix:
|
||||
return
|
||||
|
||||
if parsed_args.resource_list:
|
||||
self._load_resource_list()
|
||||
return
|
||||
|
||||
self._load_saved_state()
|
||||
|
||||
def _cleanup(self):
|
||||
LOG.info("Begin cleanup")
|
||||
is_dry_run = self.options.dry_run
|
||||
is_preserve = not self.options.delete_tempest_conf_objects
|
||||
is_resource_list = self.options.resource_list
|
||||
is_save_state = False
|
||||
cleanup_prefix = self.options.prefix
|
||||
|
||||
@ -194,8 +221,10 @@ class TempestCleanup(command.Command):
|
||||
# they are in saved state json. Therefore is_preserve is False
|
||||
kwargs = {'data': self.dry_run_data,
|
||||
'is_dry_run': is_dry_run,
|
||||
'resource_list_json': self.resource_data,
|
||||
'saved_state_json': self.json_data,
|
||||
'is_preserve': False,
|
||||
'is_resource_list': is_resource_list,
|
||||
'is_save_state': is_save_state,
|
||||
'prefix': cleanup_prefix}
|
||||
project_service = cleanup_service.ProjectService(admin_mgr, **kwargs)
|
||||
@ -208,8 +237,10 @@ class TempestCleanup(command.Command):
|
||||
|
||||
kwargs = {'data': self.dry_run_data,
|
||||
'is_dry_run': is_dry_run,
|
||||
'resource_list_json': self.resource_data,
|
||||
'saved_state_json': self.json_data,
|
||||
'is_preserve': is_preserve,
|
||||
'is_resource_list': is_resource_list,
|
||||
'is_save_state': is_save_state,
|
||||
'prefix': cleanup_prefix,
|
||||
'got_exceptions': self.GOT_EXCEPTIONS}
|
||||
@ -228,11 +259,17 @@ class TempestCleanup(command.Command):
|
||||
f.write(json.dumps(self.dry_run_data, sort_keys=True,
|
||||
indent=2, separators=(',', ': ')))
|
||||
|
||||
if is_resource_list:
|
||||
LOG.info("Clearing 'resource_list.json' file.")
|
||||
with open(RESOURCE_LIST_JSON, 'w') as f:
|
||||
f.write('{}')
|
||||
|
||||
def _clean_project(self, project):
|
||||
LOG.debug("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
|
||||
is_resource_list = self.options.resource_list
|
||||
project_id = project['id']
|
||||
project_name = project['name']
|
||||
project_data = None
|
||||
@ -244,7 +281,9 @@ class TempestCleanup(command.Command):
|
||||
kwargs = {'data': project_data,
|
||||
'is_dry_run': is_dry_run,
|
||||
'saved_state_json': self.json_data,
|
||||
'resource_list_json': self.resource_data,
|
||||
'is_preserve': is_preserve,
|
||||
'is_resource_list': is_resource_list,
|
||||
'is_save_state': False,
|
||||
'project_id': project_id,
|
||||
'prefix': cleanup_prefix,
|
||||
@ -287,6 +326,19 @@ class TempestCleanup(command.Command):
|
||||
"ignored when --init-saved-state is used so that "
|
||||
"it can capture the true init state - all "
|
||||
"resources present at that moment.")
|
||||
parser.add_argument('--resource-list', action="store_true",
|
||||
dest='resource_list', default=False,
|
||||
help="Runs tempest cleanup with generated "
|
||||
"JSON file: " + RESOURCE_LIST_JSON + " to "
|
||||
"erase resources created during Tempest run. "
|
||||
"NOTE: To create " + RESOURCE_LIST_JSON + " "
|
||||
"set config option record_resources under default "
|
||||
"section in tempest.conf file to true. 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. "
|
||||
"This option will be ignored if passed with "
|
||||
"--prefix.")
|
||||
return parser
|
||||
|
||||
def get_description(self):
|
||||
@ -304,6 +356,7 @@ class TempestCleanup(command.Command):
|
||||
'is_dry_run': False,
|
||||
'saved_state_json': data,
|
||||
'is_preserve': False,
|
||||
'is_resource_list': False,
|
||||
'is_save_state': True,
|
||||
# must be None as we want to capture true init state
|
||||
# (all resources present) thus no filtering based
|
||||
@ -326,15 +379,31 @@ class TempestCleanup(command.Command):
|
||||
f.write(json.dumps(data, sort_keys=True,
|
||||
indent=2, separators=(',', ': ')))
|
||||
|
||||
def _load_json(self, saved_state_json=SAVED_STATE_JSON):
|
||||
def _load_resource_list(self, resource_list_json=RESOURCE_LIST_JSON):
|
||||
try:
|
||||
with open(resource_list_json, 'rb') as json_file:
|
||||
self.resource_data = json.load(json_file)
|
||||
except IOError as ex:
|
||||
LOG.exception(
|
||||
"Failed loading 'resource_list.json', please "
|
||||
"be sure you created this file by setting config "
|
||||
"option record_resources in default section to true "
|
||||
"prior to running tempest. Exception: %s", ex)
|
||||
sys.exit(ex)
|
||||
except Exception as ex:
|
||||
LOG.exception(
|
||||
"Exception parsing 'resource_list.json' : %s", ex)
|
||||
sys.exit(ex)
|
||||
|
||||
def _load_saved_state(self, saved_state_json=SAVED_STATE_JSON):
|
||||
try:
|
||||
with open(saved_state_json, 'rb') as json_file:
|
||||
self.json_data = json.load(json_file)
|
||||
|
||||
except IOError as ex:
|
||||
LOG.exception("Failed loading saved state, please be sure you"
|
||||
" have first run cleanup with --init-saved-state "
|
||||
"flag prior to running tempest. Exception: %s", ex)
|
||||
LOG.exception(
|
||||
"Failed loading saved state, please be sure you"
|
||||
" have first run cleanup with --init-saved-state "
|
||||
"flag prior to running tempest. Exception: %s", ex)
|
||||
sys.exit(ex)
|
||||
except Exception as ex:
|
||||
LOG.exception("Exception parsing saved state json : %s", ex)
|
||||
|
@ -120,6 +120,13 @@ class BaseService(object):
|
||||
if item['name'].startswith(self.prefix)]
|
||||
return items
|
||||
|
||||
def _filter_by_resource_list(self, item_list, attr):
|
||||
if attr not in self.resource_list_json:
|
||||
return []
|
||||
items = [item for item in item_list if item['id']
|
||||
in self.resource_list_json[attr].keys()]
|
||||
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()]
|
||||
@ -166,8 +173,11 @@ class SnapshotService(BaseService):
|
||||
def list(self):
|
||||
client = self.client
|
||||
snaps = client.list_snapshots()['snapshots']
|
||||
|
||||
if self.prefix:
|
||||
snaps = self._filter_by_prefix(snaps)
|
||||
elif self.is_resource_list:
|
||||
snaps = self._filter_by_resource_list(snaps, 'snapshots')
|
||||
elif not self.is_save_state:
|
||||
# recreate list removing saved snapshots
|
||||
snaps = self._filter_out_ids_from_saved(snaps, 'snapshots')
|
||||
@ -205,8 +215,11 @@ class ServerService(BaseService):
|
||||
client = self.client
|
||||
servers_body = client.list_servers()
|
||||
servers = servers_body['servers']
|
||||
|
||||
if self.prefix:
|
||||
servers = self._filter_by_prefix(servers)
|
||||
elif self.is_resource_list:
|
||||
servers = self._filter_by_resource_list(servers, 'servers')
|
||||
elif not self.is_save_state:
|
||||
# recreate list removing saved servers
|
||||
servers = self._filter_out_ids_from_saved(servers, 'servers')
|
||||
@ -238,9 +251,12 @@ class ServerGroupService(ServerService):
|
||||
|
||||
def list(self):
|
||||
client = self.server_groups_client
|
||||
sgs = client.list_server_groups()['server_groups']
|
||||
sgs = client.list_server_groups(all_projects=True)['server_groups']
|
||||
|
||||
if self.prefix:
|
||||
sgs = self._filter_by_prefix(sgs)
|
||||
elif self.is_resource_list:
|
||||
sgs = self._filter_by_resource_list(sgs, 'server_groups')
|
||||
elif not self.is_save_state:
|
||||
# recreate list removing saved server_groups
|
||||
sgs = self._filter_out_ids_from_saved(sgs, 'server_groups')
|
||||
@ -276,8 +292,13 @@ class KeyPairService(BaseService):
|
||||
def list(self):
|
||||
client = self.client
|
||||
keypairs = client.list_keypairs()['keypairs']
|
||||
|
||||
if self.prefix:
|
||||
keypairs = self._filter_by_prefix(keypairs)
|
||||
elif self.is_resource_list:
|
||||
keypairs = [keypair for keypair in keypairs
|
||||
if keypair['keypair']['name']
|
||||
in self.resource_list_json['keypairs'].keys()]
|
||||
elif not self.is_save_state:
|
||||
# recreate list removing saved keypairs
|
||||
keypairs = [keypair for keypair in keypairs
|
||||
@ -317,8 +338,11 @@ class VolumeService(BaseService):
|
||||
def list(self):
|
||||
client = self.client
|
||||
vols = client.list_volumes()['volumes']
|
||||
|
||||
if self.prefix:
|
||||
vols = self._filter_by_prefix(vols)
|
||||
elif self.is_resource_list:
|
||||
vols = self._filter_by_resource_list(vols, 'volumes')
|
||||
elif not self.is_save_state:
|
||||
# recreate list removing saved volumes
|
||||
vols = self._filter_out_ids_from_saved(vols, 'volumes')
|
||||
@ -462,8 +486,11 @@ class NetworkService(BaseNetworkService):
|
||||
client = self.networks_client
|
||||
networks = client.list_networks(**self.tenant_filter)
|
||||
networks = networks['networks']
|
||||
|
||||
if self.prefix:
|
||||
networks = self._filter_by_prefix(networks)
|
||||
elif self.is_resource_list:
|
||||
networks = self._filter_by_resource_list(networks, 'networks')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
# recreate list removing saved networks
|
||||
@ -500,15 +527,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:
|
||||
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 []
|
||||
elif self.is_resource_list:
|
||||
flips = self._filter_by_resource_list(flips, 'floatingips')
|
||||
elif not self.is_save_state:
|
||||
# recreate list removing saved flips
|
||||
flips = self._filter_out_ids_from_saved(flips, 'floatingips')
|
||||
LOG.debug("List count, %s Network Floating IPs", len(flips))
|
||||
@ -543,8 +572,11 @@ class NetworkRouterService(BaseNetworkService):
|
||||
client = self.routers_client
|
||||
routers = client.list_routers(**self.tenant_filter)
|
||||
routers = routers['routers']
|
||||
|
||||
if self.prefix:
|
||||
routers = self._filter_by_prefix(routers)
|
||||
elif self.is_resource_list:
|
||||
routers = self._filter_by_resource_list(routers, 'routers')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
# recreate list removing saved routers
|
||||
@ -592,16 +624,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:
|
||||
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 []
|
||||
elif self.is_resource_list:
|
||||
rules = self._filter_by_resource_list(
|
||||
rules, 'metering_label_rules')
|
||||
elif not self.is_save_state:
|
||||
rules = self._filter_out_ids_from_saved(
|
||||
rules, 'metering_label_rules')
|
||||
# recreate list removing saved rules
|
||||
@ -638,8 +673,12 @@ class NetworkMeteringLabelService(BaseNetworkService):
|
||||
labels = client.list_metering_labels()
|
||||
labels = labels['metering_labels']
|
||||
labels = self._filter_by_tenant_id(labels)
|
||||
|
||||
if self.prefix:
|
||||
labels = self._filter_by_prefix(labels)
|
||||
elif self.is_resource_list:
|
||||
labels = self._filter_by_resource_list(
|
||||
labels, 'metering_labels')
|
||||
elif not self.is_save_state:
|
||||
# recreate list removing saved labels
|
||||
labels = self._filter_out_ids_from_saved(
|
||||
@ -677,8 +716,11 @@ class NetworkPortService(BaseNetworkService):
|
||||
client.list_ports(**self.tenant_filter)['ports']
|
||||
if port["device_owner"] == "" or
|
||||
port["device_owner"].startswith("compute:")]
|
||||
|
||||
if self.prefix:
|
||||
ports = self._filter_by_prefix(ports)
|
||||
elif self.is_resource_list:
|
||||
ports = self._filter_by_resource_list(ports, 'ports')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
# recreate list removing saved ports
|
||||
@ -717,8 +759,12 @@ class NetworkSecGroupService(BaseNetworkService):
|
||||
secgroups = [secgroup for secgroup in
|
||||
client.list_security_groups(**filter)['security_groups']
|
||||
if secgroup['name'] != 'default']
|
||||
|
||||
if self.prefix:
|
||||
secgroups = self._filter_by_prefix(secgroups)
|
||||
elif self.is_resource_list:
|
||||
secgroups = self._filter_by_resource_list(
|
||||
secgroups, 'security_groups')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
# recreate list removing saved security_groups
|
||||
@ -760,8 +806,11 @@ class NetworkSubnetService(BaseNetworkService):
|
||||
client = self.subnets_client
|
||||
subnets = client.list_subnets(**self.tenant_filter)
|
||||
subnets = subnets['subnets']
|
||||
|
||||
if self.prefix:
|
||||
subnets = self._filter_by_prefix(subnets)
|
||||
elif self.is_resource_list:
|
||||
subnets = self._filter_by_resource_list(subnets, 'subnets')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
# recreate list removing saved subnets
|
||||
@ -797,8 +846,11 @@ class NetworkSubnetPoolsService(BaseNetworkService):
|
||||
def list(self):
|
||||
client = self.subnetpools_client
|
||||
pools = client.list_subnetpools(**self.tenant_filter)['subnetpools']
|
||||
|
||||
if self.prefix:
|
||||
pools = self._filter_by_prefix(pools)
|
||||
elif self.is_resource_list:
|
||||
pools = self._filter_by_resource_list(pools, 'subnetpools')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
# recreate list removing saved subnet pools
|
||||
@ -838,13 +890,18 @@ class RegionService(BaseService):
|
||||
self.client = manager.regions_client
|
||||
|
||||
def list(self):
|
||||
client = self.client
|
||||
regions = client.list_regions()
|
||||
|
||||
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:
|
||||
elif self.is_resource_list:
|
||||
regions = self._filter_by_resource_list(
|
||||
regions['regions'], 'regions')
|
||||
return regions
|
||||
elif not self.is_save_state:
|
||||
regions = self._filter_out_ids_from_saved(
|
||||
regions['regions'], 'regions')
|
||||
LOG.debug("List count, %s Regions", len(regions))
|
||||
@ -884,8 +941,11 @@ class FlavorService(BaseService):
|
||||
def list(self):
|
||||
client = self.client
|
||||
flavors = client.list_flavors({"is_public": None})['flavors']
|
||||
|
||||
if self.prefix:
|
||||
flavors = self._filter_by_prefix(flavors)
|
||||
elif self.is_resource_list:
|
||||
flavors = self._filter_by_resource_list(flavors, 'flavors')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
# recreate list removing saved flavors
|
||||
@ -932,8 +992,11 @@ class ImageService(BaseService):
|
||||
marker = urllib.parse_qs(parsed.query)['marker'][0]
|
||||
response = client.list_images(params={"marker": marker})
|
||||
images.extend(response['images'])
|
||||
|
||||
if self.prefix:
|
||||
images = self._filter_by_prefix(images)
|
||||
elif self.is_resource_list:
|
||||
images = self._filter_by_resource_list(images, 'images')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
images = self._filter_out_ids_from_saved(images, 'images')
|
||||
@ -974,6 +1037,8 @@ class UserService(BaseService):
|
||||
users = self.client.list_users()['users']
|
||||
if self.prefix:
|
||||
users = self._filter_by_prefix(users)
|
||||
elif self.is_resource_list:
|
||||
users = self._filter_by_resource_list(users, 'users')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
users = self._filter_out_ids_from_saved(users, 'users')
|
||||
@ -1015,8 +1080,11 @@ class RoleService(BaseService):
|
||||
def list(self):
|
||||
try:
|
||||
roles = self.client.list_roles()['roles']
|
||||
|
||||
if self.prefix:
|
||||
roles = self._filter_by_prefix(roles)
|
||||
elif self.is_resource_list:
|
||||
roles = self._filter_by_resource_list(roles, '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')
|
||||
@ -1056,8 +1124,11 @@ class ProjectService(BaseService):
|
||||
|
||||
def list(self):
|
||||
projects = self.client.list_projects()['projects']
|
||||
|
||||
if self.prefix:
|
||||
projects = self._filter_by_prefix(projects)
|
||||
elif self.is_resource_list:
|
||||
projects = self._filter_by_resource_list(projects, 'projects')
|
||||
else:
|
||||
if not self.is_save_state:
|
||||
projects = self._filter_out_ids_from_saved(
|
||||
@ -1099,8 +1170,11 @@ class DomainService(BaseService):
|
||||
def list(self):
|
||||
client = self.client
|
||||
domains = client.list_domains()['domains']
|
||||
|
||||
if self.prefix:
|
||||
domains = self._filter_by_prefix(domains)
|
||||
elif self.is_resource_list:
|
||||
domains = self._filter_by_resource_list(domains, '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))
|
||||
|
@ -1309,6 +1309,15 @@ or
|
||||
"to cleanup only the resources that match the prefix. "
|
||||
"Make sure this prefix does not match with the resource "
|
||||
"name you do not want Tempest cleanup CLI to delete."),
|
||||
cfg.BoolOpt('record_resources',
|
||||
default=False,
|
||||
help="Allows to record all resources created by Tempest. "
|
||||
"These resources are stored in file resource_list.json, "
|
||||
"which can be later used for resource deletion by "
|
||||
"command tempest cleanup. The resource_list.json file "
|
||||
"will be appended in case of multiple Tempest runs, "
|
||||
"so the file will contain a list of resources created "
|
||||
"during all Tempest runs."),
|
||||
]
|
||||
|
||||
_opts = [
|
||||
|
@ -21,6 +21,7 @@ import time
|
||||
import urllib
|
||||
import urllib3
|
||||
|
||||
from fasteners import process_lock
|
||||
import jsonschema
|
||||
from oslo_log import log as logging
|
||||
from oslo_log import versionutils
|
||||
@ -78,6 +79,17 @@ class RestClient(object):
|
||||
# The version of the API this client implements
|
||||
api_version = None
|
||||
|
||||
# Directory for storing read-write lock
|
||||
lock_dir = None
|
||||
|
||||
# An interprocess lock used when the recording of all resources created by
|
||||
# Tempest is allowed.
|
||||
rec_rw_lock = None
|
||||
|
||||
# Variable mirrors value in config option 'record_resources' that allows
|
||||
# the recording of all resources created by Tempest.
|
||||
record_resources = False
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
def __init__(self, auth_provider, service, region,
|
||||
@ -297,7 +309,13 @@ class RestClient(object):
|
||||
and the second the response body
|
||||
:rtype: tuple
|
||||
"""
|
||||
return self.request('POST', url, extra_headers, headers, body, chunked)
|
||||
resp_header, resp_body = self.request(
|
||||
'POST', url, extra_headers, headers, body, chunked)
|
||||
|
||||
if self.record_resources:
|
||||
self.resource_record(resp_body)
|
||||
|
||||
return resp_header, resp_body
|
||||
|
||||
def get(self, url, headers=None, extra_headers=False, chunked=False):
|
||||
"""Send a HTTP GET request using keystone service catalog and auth
|
||||
@ -1006,6 +1024,66 @@ class RestClient(object):
|
||||
"""Returns the primary type of resource this client works with."""
|
||||
return 'resource'
|
||||
|
||||
def resource_update(self, data, res_type, res_dict):
|
||||
"""Updates resource_list.json file with current resource."""
|
||||
if not isinstance(res_dict, dict):
|
||||
return
|
||||
|
||||
if not res_type.endswith('s'):
|
||||
res_type += 's'
|
||||
|
||||
if res_type not in data:
|
||||
data[res_type] = {}
|
||||
|
||||
if 'uuid' in res_dict:
|
||||
data[res_type].update(
|
||||
{res_dict.get('uuid'): res_dict.get('name')})
|
||||
elif 'id' in res_dict:
|
||||
data[res_type].update(
|
||||
{res_dict.get('id'): res_dict.get('name')})
|
||||
elif 'name' in res_dict:
|
||||
data[res_type].update({res_dict.get('name'): ""})
|
||||
|
||||
self.rec_rw_lock.acquire_write_lock()
|
||||
with open("resource_list.json", 'w+') as f:
|
||||
f.write(json.dumps(data, indent=2, separators=(',', ': ')))
|
||||
self.rec_rw_lock.release_write_lock()
|
||||
|
||||
def resource_record(self, resp_dict):
|
||||
"""Records resources into resource_list.json file."""
|
||||
if self.rec_rw_lock is None:
|
||||
path = self.lock_dir
|
||||
self.rec_rw_lock = (
|
||||
process_lock.InterProcessReaderWriterLock(path)
|
||||
)
|
||||
|
||||
self.rec_rw_lock.acquire_read_lock()
|
||||
try:
|
||||
with open('resource_list.json', 'rb') as f:
|
||||
data = json.load(f)
|
||||
except IOError:
|
||||
data = {}
|
||||
self.rec_rw_lock.release_read_lock()
|
||||
|
||||
try:
|
||||
resp_dict = json.loads(resp_dict.decode('utf-8'))
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
return
|
||||
|
||||
# check if response has any keys
|
||||
if not resp_dict.keys():
|
||||
return
|
||||
|
||||
resource_type = list(resp_dict.keys())[0]
|
||||
|
||||
resource_dict = resp_dict[resource_type]
|
||||
|
||||
if isinstance(resource_dict, list):
|
||||
for resource in resource_dict:
|
||||
self.resource_update(data, resource_type, resource)
|
||||
else:
|
||||
self.resource_update(data, resource_type, resource_dict)
|
||||
|
||||
@classmethod
|
||||
def validate_response(cls, schema, resp, body):
|
||||
# Only check the response if the status code is a success code
|
||||
|
@ -14,6 +14,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from urllib import parse as urllib
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
from tempest.lib.api_schema.response.compute.v2_1 import server_groups \
|
||||
@ -55,9 +57,14 @@ class ServerGroupsClient(base_compute_client.BaseComputeClient):
|
||||
self.validate_response(schema.delete_server_group, resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_server_groups(self):
|
||||
def list_server_groups(self, **params):
|
||||
"""List the server-groups."""
|
||||
resp, body = self.get("os-server-groups")
|
||||
|
||||
url = 'os-server-groups'
|
||||
if params:
|
||||
url += '?%s' % urllib.urlencode(params)
|
||||
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
schema = self.get_schema(self.schema_versions_info)
|
||||
self.validate_response(schema.list_server_groups, resp, body)
|
||||
|
@ -12,6 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
from tempest.cmd import cleanup
|
||||
@ -20,12 +21,30 @@ from tempest.tests import base
|
||||
|
||||
class TestTempestCleanup(base.TestCase):
|
||||
|
||||
def test_load_json(self):
|
||||
def test_load_json_saved_state(self):
|
||||
# instantiate "empty" TempestCleanup
|
||||
c = cleanup.TempestCleanup(None, None, 'test')
|
||||
test_saved_json = 'tempest/tests/cmd/test_saved_state_json.json'
|
||||
with open(test_saved_json, 'r') as f:
|
||||
test_saved_json_content = json.load(f)
|
||||
# test if the file is loaded without any issues/exceptions
|
||||
c._load_json(test_saved_json)
|
||||
c.options = mock.Mock()
|
||||
c.options.init_saved_state = True
|
||||
c._load_saved_state(test_saved_json)
|
||||
self.assertEqual(c.json_data, test_saved_json_content)
|
||||
|
||||
def test_load_json_resource_list(self):
|
||||
# instantiate "empty" TempestCleanup
|
||||
c = cleanup.TempestCleanup(None, None, 'test')
|
||||
test_resource_list = 'tempest/tests/cmd/test_resource_list.json'
|
||||
with open(test_resource_list, 'r') as f:
|
||||
test_resource_list_content = json.load(f)
|
||||
# test if the file is loaded without any issues/exceptions
|
||||
c.options = mock.Mock()
|
||||
c.options.init_saved_state = False
|
||||
c.options.resource_list = True
|
||||
c._load_resource_list(test_resource_list)
|
||||
self.assertEqual(c.resource_data, test_resource_list_content)
|
||||
|
||||
@mock.patch('tempest.cmd.cleanup.TempestCleanup.init')
|
||||
@mock.patch('tempest.cmd.cleanup.TempestCleanup._cleanup')
|
||||
|
@ -41,8 +41,10 @@ class TestBaseService(base.TestCase):
|
||||
def test_base_service_init(self):
|
||||
kwargs = {'data': {'data': 'test'},
|
||||
'is_dry_run': False,
|
||||
'resource_list_json': {'resp': 'data'},
|
||||
'saved_state_json': {'saved': 'data'},
|
||||
'is_preserve': False,
|
||||
'is_resource_list': False,
|
||||
'is_save_state': True,
|
||||
'prefix': 'tempest',
|
||||
'tenant_id': 'project_id',
|
||||
@ -50,8 +52,10 @@ class TestBaseService(base.TestCase):
|
||||
base = cleanup_service.BaseService(kwargs)
|
||||
self.assertEqual(base.data, kwargs['data'])
|
||||
self.assertFalse(base.is_dry_run)
|
||||
self.assertEqual(base.resource_list_json, kwargs['resource_list_json'])
|
||||
self.assertEqual(base.saved_state_json, kwargs['saved_state_json'])
|
||||
self.assertFalse(base.is_preserve)
|
||||
self.assertFalse(base.is_resource_list)
|
||||
self.assertTrue(base.is_save_state)
|
||||
self.assertEqual(base.tenant_filter['project_id'], kwargs['tenant_id'])
|
||||
self.assertEqual(base.got_exceptions, kwargs['got_exceptions'])
|
||||
@ -60,8 +64,10 @@ class TestBaseService(base.TestCase):
|
||||
def test_not_implemented_ex(self):
|
||||
kwargs = {'data': {'data': 'test'},
|
||||
'is_dry_run': False,
|
||||
'resource_list_json': {'resp': 'data'},
|
||||
'saved_state_json': {'saved': 'data'},
|
||||
'is_preserve': False,
|
||||
'is_resource_list': False,
|
||||
'is_save_state': False,
|
||||
'prefix': 'tempest',
|
||||
'tenant_id': 'project_id',
|
||||
@ -181,10 +187,20 @@ class BaseCmdServiceTests(MockFunctionsBase):
|
||||
"subnetpools": {'8acf64c1-43fc': 'saved-subnet-pool'},
|
||||
"regions": {'RegionOne': {}}
|
||||
}
|
||||
|
||||
resource_list = {
|
||||
"keypairs": {'saved-key-pair': ""}
|
||||
}
|
||||
|
||||
# Mocked methods
|
||||
get_method = 'tempest.lib.common.rest_client.RestClient.get'
|
||||
delete_method = 'tempest.lib.common.rest_client.RestClient.delete'
|
||||
log_method = 'tempest.cmd.cleanup_service.LOG.exception'
|
||||
filter_saved_state = 'tempest.cmd.cleanup_service.' \
|
||||
'BaseService._filter_out_ids_from_saved'
|
||||
filter_resource_list = 'tempest.cmd.cleanup_service.' \
|
||||
'BaseService._filter_by_resource_list'
|
||||
filter_prefix = 'tempest.cmd.cleanup_service.BaseService._filter_by_prefix'
|
||||
# Override parameters
|
||||
service_class = 'BaseService'
|
||||
response = None
|
||||
@ -192,17 +208,19 @@ class BaseCmdServiceTests(MockFunctionsBase):
|
||||
|
||||
def _create_cmd_service(self, service_type, is_save_state=False,
|
||||
is_preserve=False, is_dry_run=False,
|
||||
prefix=''):
|
||||
prefix='', is_resource_list=False):
|
||||
creds = fake_credentials.FakeKeystoneV3Credentials()
|
||||
os = clients.Manager(creds)
|
||||
return getattr(cleanup_service, service_type)(
|
||||
os,
|
||||
is_resource_list=is_resource_list,
|
||||
is_save_state=is_save_state,
|
||||
is_preserve=is_preserve,
|
||||
is_dry_run=is_dry_run,
|
||||
prefix=prefix,
|
||||
project_id='b8e3ece07bb049138d224436756e3b57',
|
||||
data={},
|
||||
resource_list_json=self.resource_list,
|
||||
saved_state_json=self.saved_state
|
||||
)
|
||||
|
||||
@ -266,6 +284,38 @@ class BaseCmdServiceTests(MockFunctionsBase):
|
||||
self.assertNotIn(rsp['id'], self.conf_values.values())
|
||||
self.assertNotIn(rsp['name'], self.conf_values.values())
|
||||
|
||||
def _test_prefix_opt_precedence(self, delete_mock):
|
||||
serv = self._create_cmd_service(
|
||||
self.service_class, is_resource_list=True, prefix='tempest')
|
||||
_, fixtures = self.run_function_with_mocks(
|
||||
serv.run,
|
||||
delete_mock
|
||||
)
|
||||
|
||||
# Check that prefix was used for filtering
|
||||
fixtures[2].mock.assert_called_once()
|
||||
|
||||
# Check that neither saved_state.json nor resource list was
|
||||
# used for filtering
|
||||
fixtures[0].mock.assert_not_called()
|
||||
fixtures[1].mock.assert_not_called()
|
||||
|
||||
def _test_resource_list_opt_precedence(self, delete_mock):
|
||||
serv = self._create_cmd_service(
|
||||
self.service_class, is_resource_list=True)
|
||||
_, fixtures = self.run_function_with_mocks(
|
||||
serv.run,
|
||||
delete_mock
|
||||
)
|
||||
|
||||
# Check that resource list was used for filtering
|
||||
fixtures[1].mock.assert_called_once()
|
||||
|
||||
# Check that neither saved_state.json nor prefix was
|
||||
# used for filtering
|
||||
fixtures[0].mock.assert_not_called()
|
||||
fixtures[2].mock.assert_not_called()
|
||||
|
||||
|
||||
class TestSnapshotService(BaseCmdServiceTests):
|
||||
|
||||
@ -320,6 +370,24 @@ class TestSnapshotService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestServerService(BaseCmdServiceTests):
|
||||
|
||||
@ -378,6 +446,24 @@ class TestServerService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestServerGroupService(BaseCmdServiceTests):
|
||||
|
||||
@ -429,6 +515,26 @@ class TestServerGroupService(BaseCmdServiceTests):
|
||||
(self.validate_response, 'validate', None)
|
||||
])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.validate_response, 'validate', None),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.validate_response, 'validate', None),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestKeyPairService(BaseCmdServiceTests):
|
||||
|
||||
@ -493,6 +599,33 @@ class TestKeyPairService(BaseCmdServiceTests):
|
||||
(self.validate_response, 'validate', None)
|
||||
])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.validate_response, 'validate', None),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.validate_response, 'validate', None),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
serv = self._create_cmd_service(
|
||||
self.service_class, is_resource_list=True)
|
||||
|
||||
_, fixtures = self.run_function_with_mocks(
|
||||
serv.delete,
|
||||
delete_mock
|
||||
)
|
||||
|
||||
# Check that prefix was not used for filtering
|
||||
fixtures[0].mock.assert_not_called()
|
||||
|
||||
|
||||
class TestVolumeService(BaseCmdServiceTests):
|
||||
|
||||
@ -542,6 +675,24 @@ class TestVolumeService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestVolumeQuotaService(BaseCmdServiceTests):
|
||||
|
||||
@ -761,6 +912,24 @@ class TestNetworkService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkFloatingIpService(BaseCmdServiceTests):
|
||||
|
||||
@ -823,6 +992,34 @@ class TestNetworkFloatingIpService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
serv = self._create_cmd_service(
|
||||
self.service_class, is_resource_list=True, prefix='tempest')
|
||||
_, fixtures = self.run_function_with_mocks(
|
||||
serv.run,
|
||||
delete_mock
|
||||
)
|
||||
|
||||
# cleanup returns []
|
||||
fixtures[0].mock.assert_not_called()
|
||||
fixtures[1].mock.assert_not_called()
|
||||
fixtures[2].mock.assert_not_called()
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkRouterService(BaseCmdServiceTests):
|
||||
|
||||
@ -937,6 +1134,24 @@ class TestNetworkRouterService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkMeteringLabelRuleService(BaseCmdServiceTests):
|
||||
|
||||
@ -978,6 +1193,34 @@ class TestNetworkMeteringLabelRuleService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
serv = self._create_cmd_service(
|
||||
self.service_class, is_resource_list=True, prefix='tempest')
|
||||
_, fixtures = self.run_function_with_mocks(
|
||||
serv.run,
|
||||
delete_mock
|
||||
)
|
||||
|
||||
# cleanup returns []
|
||||
fixtures[0].mock.assert_not_called()
|
||||
fixtures[1].mock.assert_not_called()
|
||||
fixtures[2].mock.assert_not_called()
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkMeteringLabelService(BaseCmdServiceTests):
|
||||
|
||||
@ -1020,6 +1263,24 @@ class TestNetworkMeteringLabelService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkPortService(BaseCmdServiceTests):
|
||||
|
||||
@ -1118,6 +1379,24 @@ class TestNetworkPortService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkSecGroupService(BaseCmdServiceTests):
|
||||
|
||||
@ -1196,6 +1475,24 @@ class TestNetworkSecGroupService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkSubnetService(BaseCmdServiceTests):
|
||||
|
||||
@ -1272,6 +1569,24 @@ class TestNetworkSubnetService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestNetworkSubnetPoolsService(BaseCmdServiceTests):
|
||||
|
||||
@ -1340,6 +1655,24 @@ class TestNetworkSubnetPoolsService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
# begin global services
|
||||
class TestRegionService(BaseCmdServiceTests):
|
||||
@ -1392,6 +1725,34 @@ class TestRegionService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
serv = self._create_cmd_service(
|
||||
self.service_class, is_resource_list=True, prefix='tempest')
|
||||
_, fixtures = self.run_function_with_mocks(
|
||||
serv.run,
|
||||
delete_mock
|
||||
)
|
||||
|
||||
# cleanup returns []
|
||||
fixtures[0].mock.assert_not_called()
|
||||
fixtures[1].mock.assert_not_called()
|
||||
fixtures[2].mock.assert_not_called()
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestDomainService(BaseCmdServiceTests):
|
||||
|
||||
@ -1445,6 +1806,26 @@ class TestDomainService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None),
|
||||
(self.mock_update, 'update', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None),
|
||||
(self.mock_update, 'update', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestProjectsService(BaseCmdServiceTests):
|
||||
|
||||
@ -1518,6 +1899,24 @@ class TestProjectsService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestImagesService(BaseCmdServiceTests):
|
||||
|
||||
@ -1597,6 +1996,24 @@ class TestImagesService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestFlavorService(BaseCmdServiceTests):
|
||||
|
||||
@ -1670,6 +2087,24 @@ class TestFlavorService(BaseCmdServiceTests):
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestRoleService(BaseCmdServiceTests):
|
||||
|
||||
@ -1716,6 +2151,24 @@ class TestRoleService(BaseCmdServiceTests):
|
||||
def test_save_state(self):
|
||||
self._test_saved_state_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
||||
|
||||
class TestUserService(BaseCmdServiceTests):
|
||||
|
||||
@ -1782,3 +2235,21 @@ class TestUserService(BaseCmdServiceTests):
|
||||
"password_expires_at": "1893-11-06T15:32:17.000000",
|
||||
})
|
||||
self._test_is_preserve_true([(self.get_method, self.response, 200)])
|
||||
|
||||
def test_prefix_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_prefix_opt_precedence(delete_mock)
|
||||
|
||||
def test_resource_list_opt_precedence(self):
|
||||
delete_mock = [(self.filter_saved_state, [], None),
|
||||
(self.filter_resource_list, [], None),
|
||||
(self.filter_prefix, [], None),
|
||||
(self.get_method, self.response, 200),
|
||||
(self.delete_method, 'error', None),
|
||||
(self.log_method, 'exception', None)]
|
||||
self._test_resource_list_opt_precedence(delete_mock)
|
||||
|
11
tempest/tests/cmd/test_resource_list.json
Normal file
11
tempest/tests/cmd/test_resource_list.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"project": {
|
||||
"ce4e7edf051c439d8b81c4bfe581c5ef": "test"
|
||||
},
|
||||
"keypairs": {
|
||||
"tempest-keypair-1215039183": ""
|
||||
},
|
||||
"users": {
|
||||
"74463c83f9d640fe84c4376527ceff26": "test"
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
from unittest import mock
|
||||
|
||||
import fixtures
|
||||
import jsonschema
|
||||
@ -749,6 +750,110 @@ class TestExpectedSuccess(BaseRestClientTestClass):
|
||||
expected_code, read_code)
|
||||
|
||||
|
||||
class TestRecordResources(BaseRestClientTestClass):
|
||||
|
||||
def setUp(self):
|
||||
self.fake_http = fake_http.fake_httplib2()
|
||||
super(TestRecordResources, self).setUp()
|
||||
|
||||
def _cleanup_test_resource_record(self):
|
||||
# clear resource_list.json file
|
||||
with open('resource_list.json', 'w') as f:
|
||||
f.write('{}')
|
||||
|
||||
def test_post_record_resources(self):
|
||||
self.rest_client.record_resources = True
|
||||
__, return_dict = self.rest_client.post(self.url, {}, {})
|
||||
self.assertEqual({}, return_dict['headers'])
|
||||
self.assertEqual({}, return_dict['body'])
|
||||
|
||||
def test_resource_record_no_top_key(self):
|
||||
test_body_no_key = b'{}'
|
||||
self.rest_client.resource_record(test_body_no_key)
|
||||
|
||||
def test_resource_record_dict(self):
|
||||
test_dict_body = b'{"project": {"id": "test-id", "name": ""}}\n'
|
||||
self.rest_client.resource_record(test_dict_body)
|
||||
|
||||
with open('resource_list.json', 'r') as f:
|
||||
content = f.read()
|
||||
resource_list_content = json.loads(content)
|
||||
|
||||
test_resource_list = {
|
||||
"projects": {"test-id": ""}
|
||||
}
|
||||
self.assertEqual(resource_list_content, test_resource_list)
|
||||
|
||||
# cleanup
|
||||
self._cleanup_test_resource_record()
|
||||
|
||||
def test_resource_record_list(self):
|
||||
test_list_body = '''{
|
||||
"user": [
|
||||
{
|
||||
"id": "test-uuid",
|
||||
"name": "test-name"
|
||||
},
|
||||
{
|
||||
"id": "test-uuid2",
|
||||
"name": "test-name2"
|
||||
}
|
||||
]
|
||||
}'''
|
||||
test_list_body = test_list_body.encode('utf-8')
|
||||
self.rest_client.resource_record(test_list_body)
|
||||
|
||||
with open('resource_list.json', 'r') as f:
|
||||
content = f.read()
|
||||
resource_list_content = json.loads(content)
|
||||
|
||||
test_resource_list = {
|
||||
"users": {
|
||||
"test-uuid": "test-name",
|
||||
"test-uuid2": "test-name2"
|
||||
}
|
||||
}
|
||||
self.assertEqual(resource_list_content, test_resource_list)
|
||||
|
||||
# cleanup
|
||||
self._cleanup_test_resource_record()
|
||||
|
||||
def test_resource_update_id(self):
|
||||
data = {}
|
||||
res_dict = {'id': 'test-uuid', 'name': 'test-name'}
|
||||
|
||||
self.rest_client.rec_rw_lock = mock.MagicMock()
|
||||
self.rest_client.resource_update(data, 'user', res_dict)
|
||||
result = {'users': {'test-uuid': 'test-name'}}
|
||||
self.assertEqual(data, result)
|
||||
|
||||
def test_resource_update_name(self):
|
||||
data = {'keypairs': {}}
|
||||
res_dict = {'name': 'test-keypair'}
|
||||
|
||||
self.rest_client.rec_rw_lock = mock.MagicMock()
|
||||
self.rest_client.resource_update(data, 'keypair', res_dict)
|
||||
result = {'keypairs': {'test-keypair': ""}}
|
||||
self.assertEqual(data, result)
|
||||
|
||||
def test_resource_update_no_id(self):
|
||||
data = {}
|
||||
res_dict = {'type': 'test', 'description': 'example'}
|
||||
|
||||
self.rest_client.rec_rw_lock = mock.MagicMock()
|
||||
self.rest_client.resource_update(data, 'projects', res_dict)
|
||||
result = {'projects': {}}
|
||||
self.assertEqual(data, result)
|
||||
|
||||
def test_resource_update_not_dict(self):
|
||||
data = {}
|
||||
res_dict = 'test-string'
|
||||
|
||||
self.rest_client.rec_rw_lock = mock.MagicMock()
|
||||
self.rest_client.resource_update(data, 'user', res_dict)
|
||||
self.assertEqual(data, {})
|
||||
|
||||
|
||||
class TestResponseBody(base.TestCase):
|
||||
|
||||
def test_str(self):
|
||||
|
Loading…
Reference in New Issue
Block a user