diff --git a/tests/local/zaza/sunbeam/charm_tests/tempest_k8s/tests.py b/tests/local/zaza/sunbeam/charm_tests/tempest_k8s/tests.py index 061852da..53182c1d 100644 --- a/tests/local/zaza/sunbeam/charm_tests/tempest_k8s/tests.py +++ b/tests/local/zaza/sunbeam/charm_tests/tempest_k8s/tests.py @@ -13,8 +13,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging + from zaza import model from zaza.openstack.charm_tests import test_utils +from zaza.openstack.utilities import openstack as openstack_utils + + +logger = logging.getLogger(__name__) def wait_for_application_state( @@ -41,6 +47,63 @@ class TempestK8sTest(test_utils.BaseCharmTest): """Run class setup for running tests.""" super(TempestK8sTest, cls).setUpClass(application_name="tempest") + # Connect to the OpenStack cloud + keystone_session = openstack_utils.get_overcloud_keystone_session() + cls.keystone_client = openstack_utils.get_keystone_session_client(keystone_session) + cls.glance_client = openstack_utils.get_glance_session_client(keystone_session) + cls.neutron_client = openstack_utils.get_neutron_session_client(keystone_session) + + def get_tempest_init_resources(self, domain_id): + """Get the test accounts and associated resources generated by tempest. + + Return a dict of resources containing users, projects, and networks created + at tempest init stage. + """ + test_accounts_resources = dict() + + test_accounts_resources["projects"] = [ + project.name + for project in self.keystone_client.projects.list(domain=domain_id) + if project.name.startswith('tempest-') + ] + test_accounts_resources["users"] = [ + user.name + for user in self.keystone_client.users.list(domain=domain_id) + if user.name.startswith('tempest-') + ] + test_accounts_resources["networks"] = [ + network["name"] + for network in self.neutron_client.list_networks()['networks'] + if network["name"].startswith('tempest-') + ] + + return test_accounts_resources + + def get_domain_id(self): + """Get tempest domain id.""" + return openstack_utils.get_domain_id( + self.keystone_client, + domain_name="CloudValidation-b82746a08d" + ) + + def check_charm_created_resources(self, domain_id): + """Check charm created resources exists.""" + self.assertTrue(domain_id) + + projects = [ + project.name + for project in self.keystone_client.projects.list(domain=domain_id) + if project.name == "CloudValidation-test-project" + ] + users = [ + user.name + for user in self.keystone_client.users.list(domain=domain_id) + if user.name == "CloudValidation-test-user" + ] + + self.assertTrue(projects) + self.assertTrue(users) + def test_get_lists(self): """Verify that the get-lists action returns list names as expected.""" action = model.run_action_on_leader(self.application_name, "get-lists") @@ -48,9 +111,25 @@ class TempestK8sTest(test_utils.BaseCharmTest): self.assertIn("readonly-quick", lists) self.assertIn("refstack-2022.11", lists) - def test_bounce_keystone_relation(self): - """Test removing and re-adding the keystone relation.""" - # verify that the application is blocked when keystone is missing + def test_bounce_keystone_relation_with_extensive_cleanup(self): + """Test removing and re-adding the keystone relation. + + Extensive cleanup should be triggered upon keystone relation break to + remove all resources created by tempest init. Charm-created + resources gets removed and re-created upon keystone relation rejoin. + """ + # Verify the existance of charm-created resources + domain_id = self.get_domain_id() + self.check_charm_created_resources(domain_id) + + # Verify the existance of resources created by tempest init + # when keystone relation is joined + test_accounts_resources = self.get_tempest_init_resources(domain_id) + self.assertTrue(test_accounts_resources["projects"]) + self.assertTrue(test_accounts_resources["users"]) + self.assertTrue(test_accounts_resources["networks"]) + + # Verify that the application is blocked when keystone is missing model.remove_relation("tempest", "identity-ops", "keystone") wait_for_application_state( model, @@ -65,6 +144,17 @@ class TempestK8sTest(test_utils.BaseCharmTest): r"^$", ) + # Verify that charm-created resources remain in the cloud + self.assertEqual(domain_id, self.get_domain_id()) + self.check_charm_created_resources(domain_id) + + # Verify that there are no more resources created by tempest init + # when keystone relation is removed + test_accounts_resources = self.get_tempest_init_resources(domain_id) + self.assertFalse(test_accounts_resources["projects"]) + self.assertFalse(test_accounts_resources["users"]) + self.assertFalse(test_accounts_resources["networks"]) + # And then verify that adding it back # results in reaching active/idle state again. # ie. successful tempest init again. @@ -76,3 +166,60 @@ class TempestK8sTest(test_utils.BaseCharmTest): "active", r"^$", ) + + # Verify that a new domain (with project and user) is created which + # replaces the old one + new_domain_id = self.get_domain_id() + self.assertNotEqual(new_domain_id, domain_id) + self.check_charm_created_resources(new_domain_id) + + # Verify that tempest init re-created projects, users and networks when + # keystone relation is re-joined + test_accounts_resources = self.get_tempest_init_resources(new_domain_id) + self.assertTrue(test_accounts_resources["projects"]) + self.assertTrue(test_accounts_resources["users"]) + self.assertTrue(test_accounts_resources["networks"]) + + def test_quick_cleanup_in_between_tests(self): + """Verify that quick cleanup in between tests are behaving correctly. + + Test-created resources should be removed. Resources generated by charm and + tempest init remain in the cloud. + """ + # Get the list of images before test is run. Note that until an upstream + # fix [1] lands and releases, we cannot use `tempest-` prefix as the filter. + # Instead, we compare the list of all images before and after a test run. + # [1]: https://review.opendev.org/c/openstack/tempest/+/908358 + before_images = [i.name for i in self.glance_client.images.list()] + + # Get the resources (domain, projects, users, and networks) generated by + # the charm and tempest init + domain_id = self.get_domain_id() + self.check_charm_created_resources(domain_id) + before_test_accounts_resources = self.get_tempest_init_resources(domain_id) + + # Run a test that will create an image in the cloud + action = model.run_action_on_leader( + self.application_name, "validate", + action_params={ + "regex": "test_image_web_download_import_with_bad_url", + } + ) + logger.info("action.data = %s", action.data) + summary = action.data["results"]["summary"] + + # Verify that the test is successfully ran and passed. + # Successul test run means the image resource has been created. + self.assertIn("Ran: 1 tests", summary) + self.assertIn("Passed: 1", summary) + + # Verify that the image createby test is removed + after_images = [i.name for i in self.glance_client.images.list()] + self.assertEqual(after_images, before_images) + + # Verify that the resources created by charm and tempest init + # (domain, projects, users, and networks) remain intact. + self.assertEqual(domain_id, self.get_domain_id()) + self.check_charm_created_resources(domain_id) + after_test_accounts_resources = self.get_tempest_init_resources(domain_id) + self.assertEqual(after_test_accounts_resources, before_test_accounts_resources)