diff --git a/senlinclient/tests/functional/base.py b/senlinclient/tests/functional/base.py index ed6dfe29..8b13bf5d 100644 --- a/senlinclient/tests/functional/base.py +++ b/senlinclient/tests/functional/base.py @@ -12,10 +12,12 @@ import os import six +import time from oslo_utils import uuidutils from tempest.lib.cli import base from tempest.lib.cli import output_parser +from tempest.lib import exceptions as tempest_lib_exc class OpenStackClientTestBase(base.ClientTestBase): @@ -63,8 +65,56 @@ class OpenStackClientTestBase(base.ClientTestBase): return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'policies/%s' % policy_name) - def policy_create(self, name, profile): - pf = self._get_policy_path(profile) + def wait_for_status(self, name, status, check_type, timeout=60, + poll_interval=5): + """Wait until name reaches given status. + + :param name: node or cluster name + :param status: expected status of node or cluster + :param timeout: timeout in seconds + :param poll_interval: poll interval in seconds + """ + if check_type == 'node': + cmd = ('cluster node show %s' % name) + elif check_type == 'cluster': + cmd = ('cluster show %s' % name) + time.sleep(poll_interval) + start_time = time.time() + while time.time() - start_time < timeout: + check_status = self.openstack(cmd) + result = self.show_to_dict(check_status) + if result['status'] == status: + break + time.sleep(poll_interval) + else: + message = ("%s %s did not reach status %s after %d s" + % (check_type, name, status, timeout)) + raise tempest_lib_exc.TimeoutException(message) + + def wait_for_delete(self, name, check_type, timeout=60, + poll_interval=5): + """Wait until delete finish""" + if check_type == 'node': + cmd = ('cluster node show %s' % name) + if check_type == 'cluster': + cmd = ('cluster show %s' % name) + time.sleep(poll_interval) + start_time = time.time() + while time.time() - start_time < timeout: + try: + self.openstack(cmd) + except tempest_lib_exc.CommandFailed as ex: + if "No Node found" or "No Cluster found" in ex.stderr: + break + time.sleep(poll_interval) + else: + + message = ("failed in deleting %s %s after %d seconds" + % (check_type, name, timeout)) + raise tempest_lib_exc.TimeoutException(message) + + def policy_create(self, name, policy): + pf = self._get_policy_path(policy) cmd = ('cluster policy create --spec-file %s %s' % (pf, name)) policy_raw = self.openstack(cmd) @@ -75,8 +125,8 @@ class OpenStackClientTestBase(base.ClientTestBase): cmd = ('cluster policy delete %s --force' % name_or_id) self.openstack(cmd) - def profile_create(self, name, policy): - pf = self._get_profile_path(policy) + def profile_create(self, name, profile='cirros_basic.yaml'): + pf = self._get_profile_path(profile) cmd = ('cluster profile create --spec-file %s %s' % (pf, name)) profile_raw = self.openstack(cmd) @@ -86,3 +136,16 @@ class OpenStackClientTestBase(base.ClientTestBase): def profile_delete(self, name_or_id): cmd = ('cluster profile delete %s --force' % name_or_id) self.openstack(cmd) + + def node_create(self, profile, name): + cmd = ('cluster node create --profile %s %s' + % (profile, name)) + node_raw = self.openstack(cmd) + result = self.show_to_dict(node_raw) + self.wait_for_status(name, 'ACTIVE', 'node', 120) + return result + + def node_delete(self, name_or_id): + cmd = ('cluster node delete %s --force' % name_or_id) + self.openstack(cmd) + self.wait_for_delete(name_or_id, 'node', 120) diff --git a/senlinclient/tests/functional/profiles/cirros_basic.yaml b/senlinclient/tests/functional/profiles/cirros_basic.yaml index ad122a7a..e4825c96 100644 --- a/senlinclient/tests/functional/profiles/cirros_basic.yaml +++ b/senlinclient/tests/functional/profiles/cirros_basic.yaml @@ -1,7 +1,6 @@ type: os.nova.server version: 1.0 properties: - name: cirros_server flavor: 1 image: "cirros-0.3.5-x86_64-disk.img" networks: diff --git a/senlinclient/tests/functional/test_nodes.py b/senlinclient/tests/functional/test_nodes.py index f0025159..5e407b37 100644 --- a/senlinclient/tests/functional/test_nodes.py +++ b/senlinclient/tests/functional/test_nodes.py @@ -23,3 +23,79 @@ class NodeTest(base.OpenStackClientTestBase): 'cluster_id', 'physical_id', 'profile_name', 'created_at', 'updated_at']) + + def test_node_create(self): + name = self.name_generate() + pf = self.profile_create(name) + node = self.node_create(pf['id'], name) + self.assertEqual(node['name'], name) + self.node_delete(node['id']) + self.addCleanup(self.profile_delete, pf['id']) + + def test_node_update(self): + old_name = self.name_generate() + pf = self.profile_create(old_name) + n1 = self.node_create(pf['id'], old_name) + new_name = self.name_generate() + pf_new = self.profile_create(new_name) + role = 'master' + cmd = ('cluster node update --name %s --role %s --profile %s %s' + % (new_name, role, pf_new['id'], n1['id'])) + self.openstack(cmd) + self.wait_for_status(n1['id'], 'ACTIVE', 'node', 120) + raw_node = self.openstack('cluster node show %s' % n1['id']) + node_data = self.show_to_dict(raw_node) + self.assertEqual(node_data['name'], new_name) + self.assertNotEqual(node_data['name'], old_name) + self.assertEqual(node_data['role'], role) + self.assertEqual(node_data['profile_id'], pf_new['id']) + self.node_delete(new_name) + self.addCleanup(self.profile_delete, pf['id']) + self.addCleanup(self.profile_delete, pf_new['id']) + + def test_node_detail(self): + name = self.name_generate() + pf = self.profile_create(name) + node = self.node_create(pf['id'], name) + cmd = ('cluster node show --details %s' % name) + raw_node = self.openstack(cmd) + node_data = self.show_to_dict(raw_node) + self.assertIn('details', node_data) + self.assertIsNotNone(node_data['details']) + self.node_delete(node['id']) + self.addCleanup(self.profile_delete, pf['id']) + + # NOTE(Qiming): Since functional tests only focus on the client/server + # interaction without invovling other OpenStack services, it is not + # possible to mock a node failure and then test if the check logic works. + # Such tests would be left to integration tests instead. + def test_node_check(self): + name = self.name_generate() + pf = self.profile_create(name) + node = self.node_create(pf['id'], name) + cmd = ('cluster node check %s' % node['id']) + self.openstack(cmd) + check_raw = self.openstack('cluster node show %s' % name) + check_data = self.show_to_dict(check_raw) + self.assertIn('Check', check_data['status_reason']) + node_status = ['ACTIVE', 'ERROR'] + self.assertIn(check_data['status'], node_status) + self.node_delete(node['id']) + self.addCleanup(self.profile_delete, pf['id']) + + # NOTE(Qiming): A end-to-end test of the node recover operation needs to + # be done with other OpenStack services involved, thus out of scope for + # functional tests. Such tests would be left to integration tests instead. + def test_node_recover(self): + name = self.name_generate() + pf = self.profile_create(name) + node = self.node_create(pf['id'], name) + cmd = ('cluster node recover --check true %s' % node['id']) + self.openstack(cmd) + self.wait_for_status(name, 'ACTIVE', 'node', 120) + recover_raw = self.openstack('cluster node show %s' % name) + recover_data = self.show_to_dict(recover_raw) + self.assertIn('Recover', recover_data['status_reason']) + self.assertEqual('ACTIVE', recover_data['status']) + self.node_delete(node['id']) + self.addCleanup(self.profile_delete, pf['id']) diff --git a/senlinclient/tests/functional/test_policy_types.py b/senlinclient/tests/functional/test_policy_types.py index e09f4760..ab239008 100644 --- a/senlinclient/tests/functional/test_policy_types.py +++ b/senlinclient/tests/functional/test_policy_types.py @@ -19,8 +19,10 @@ class PolicyTypeTest(base.OpenStackClientTestBase): def test_policy_type_list(self): result = self.openstack('cluster policy type list') policy_type = self.parser.listing(result) - self.assertTableStruct(policy_type, ['name', 'version', - 'support_status']) + columns = ['name', 'version'] + if any('support_status' in i for i in policy_type): + columns.append('support_status') + self.assertTableStruct(policy_type, columns) def test_policy_type_show(self): params = ['senlin.policy.affinity-1.0', diff --git a/senlinclient/tests/functional/test_profile_types.py b/senlinclient/tests/functional/test_profile_types.py index 0044dfb3..8d973133 100644 --- a/senlinclient/tests/functional/test_profile_types.py +++ b/senlinclient/tests/functional/test_profile_types.py @@ -22,11 +22,13 @@ class ProfileTypeTest(base.OpenStackClientTestBase): def test_profile_type_list(self): result = self.openstack('cluster profile type list') profile_type = self.parser.listing(result) - self.assertTableStruct(profile_type, ['name', 'version', - 'support_status']) + columns = ['name', 'version'] + if any('support_status' in i for i in profile_type): + columns.append('support_status') + self.assertTableStruct(profile_type, columns) def test_profile_list_debug(self): - self.openstack('cluster profile list', flags='--debug') + self.openstack('cluster profile type list', flags='--debug') def test_profile_type_show(self): params = ['container.dockerinc.docker-1.0',