From 44f678c7a659975aac5db5f3a02f1512ff921bfa Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Thu, 17 Mar 2016 12:11:30 +0200 Subject: [PATCH 01/26] Tempest: add basic test Change-Id: I7155e797fecf18b867eeb7c63ebbcb35d3cbb9c3 Co-Authored-By: dparalen Depends-On: Ibf0c73aa6795aaa52e945fd6baa821de20a599e7 Depends-On: I067504e49f68929298c91e61819aa9a61169fe52 --- ironic_tempest_plugin/exceptions.py | 25 +++ .../rules/basic_ops_rule.json | 25 +++ .../services/introspection_client.py | 70 ++++++++ ironic_tempest_plugin/tests/manager.py | 140 ++++++++++++++++ ironic_tempest_plugin/tests/test_basic.py | 149 ++++++++++++++++++ 5 files changed, 409 insertions(+) create mode 100644 ironic_tempest_plugin/exceptions.py create mode 100644 ironic_tempest_plugin/rules/basic_ops_rule.json create mode 100644 ironic_tempest_plugin/services/introspection_client.py create mode 100644 ironic_tempest_plugin/tests/manager.py create mode 100644 ironic_tempest_plugin/tests/test_basic.py diff --git a/ironic_tempest_plugin/exceptions.py b/ironic_tempest_plugin/exceptions.py new file mode 100644 index 00000000..7791c40f --- /dev/null +++ b/ironic_tempest_plugin/exceptions.py @@ -0,0 +1,25 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest import exceptions + + +class IntrospectionFailed(exceptions.TempestException): + message = "Introspection failed" + + +class IntrospectionTimeout(exceptions.TempestException): + message = "Introspection time out" + + +class HypervisorUpdateTimeout(exceptions.TempestException): + message = "Hypervisor stats update time out" diff --git a/ironic_tempest_plugin/rules/basic_ops_rule.json b/ironic_tempest_plugin/rules/basic_ops_rule.json new file mode 100644 index 00000000..f1cfb0b2 --- /dev/null +++ b/ironic_tempest_plugin/rules/basic_ops_rule.json @@ -0,0 +1,25 @@ +[ + { + "description": "Successful Rule", + "conditions": [ + {"op": "ge", "field": "memory_mb", "value": 256}, + {"op": "ge", "field": "local_gb", "value": 1} + ], + "actions": [ + {"action": "set-attribute", "path": "/extra/rule_success", + "value": "yes"} + ] + }, + { + "description": "Failing Rule", + "conditions": [ + {"op": "lt", "field": "memory_mb", "value": 42}, + {"op": "eq", "field": "local_gb", "value": 0} + ], + "actions": [ + {"action": "set-attribute", "path": "/extra/rule_success", + "value": "no"}, + {"action": "fail", "message": "This rule should not have run"} + ] + } +] diff --git a/ironic_tempest_plugin/services/introspection_client.py b/ironic_tempest_plugin/services/introspection_client.py new file mode 100644 index 00000000..346e06c6 --- /dev/null +++ b/ironic_tempest_plugin/services/introspection_client.py @@ -0,0 +1,70 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from tempest import clients +from tempest.common import credentials_factory as common_creds +from tempest import config +from tempest.services.baremetal import base + + +CONF = config.CONF +ADMIN_CREDS = common_creds.get_configured_admin_credentials() + + +class Manager(clients.Manager): + def __init__(self, + credentials=ADMIN_CREDS, + service=None, + api_microversions=None): + super(Manager, self).__init__(credentials, service) + self.introspection_client = BaremetalIntrospectionClient( + self.auth_provider, + CONF.baremetal_introspection.catalog_type, + CONF.identity.region, + endpoint_type=CONF.baremetal_introspection.endpoint_type, + **self.default_params_with_timeout_values) + + +class BaremetalIntrospectionClient(base.BaremetalClient): + """Base Tempest REST client for Ironic Inspector API v1.""" + version = '1' + uri_prefix = 'v1' + + @base.handle_errors + def purge_rules(self): + """Purge all existing rules.""" + return self._delete_request('rules', uuid=None) + + @base.handle_errors + def import_rule(self, rule_path): + """Import introspection rules from a json file.""" + with open(rule_path, 'r') as fp: + rules = json.load(fp) + if not isinstance(rules, list): + rules = [rules] + + for rule in rules: + self._create_request('rules', rule) + + @base.handle_errors + def get_status(self, uuid): + """Get introspection status for a node.""" + return self._show_request('introspection', uuid=uuid) + + @base.handle_errors + def get_data(self, uuid): + """Get introspection data for a node.""" + return self._show_request('introspection', uuid=uuid, + uri='/%s/introspection/%s/data' % + (self.uri_prefix, uuid)) diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py new file mode 100644 index 00000000..dd2a18eb --- /dev/null +++ b/ironic_tempest_plugin/tests/manager.py @@ -0,0 +1,140 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import os +import time + +from tempest import config + +from ironic_inspector.test.inspector_tempest_plugin import exceptions +from ironic_inspector.test.inspector_tempest_plugin.services import \ + introspection_client +from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ + BaremetalScenarioTest + + +CONF = config.CONF + + +class InspectorScenarioTest(BaremetalScenarioTest): + """Provide harness to do Inspector scenario tests.""" + + credentials = ['primary', 'admin'] + + @classmethod + def setup_clients(cls): + super(InspectorScenarioTest, cls).setup_clients() + inspector_manager = introspection_client.Manager() + cls.introspection_client = inspector_manager.introspection_client + + def setUp(self): + super(InspectorScenarioTest, self).setUp() + self.flavor = self.baremetal_flavor() + + def item_filter(self, list_method, show_method, + filter=lambda item: True, items=None): + if items is None: + items = [show_method(item['uuid']) for item in + list_method()] + return [item for item in items if filter(item)] + + def node_list(self): + return self.baremetal_client.list_nodes()[1]['nodes'] + + def node_update(self, uuid, patch): + return self.baremetal_client.update_node(uuid, **patch) + + def node_show(self, uuid): + return self.baremetal_client.show_node(uuid)[1] + + def node_filter(self, filter=lambda node: True, nodes=None): + return self.item_filter(self.node_list, self.node_show, + filter=filter, items=nodes) + + def hypervisor_stats(self): + return (self.admin_manager.hypervisor_client. + show_hypervisor_statistics()) + + def server_show(self, uuid): + self.servers_client.show_server(uuid) + + def rule_purge(self): + self.introspection_client.purge_rules() + + def rule_import(self, rule_path): + self.introspection_client.import_rule(rule_path) + + def introspection_status(self, uuid): + return self.introspection_client.get_status(uuid)[1] + + def introspection_data(self, uuid): + return self.introspection_client.get_data(uuid)[1] + + def baremetal_flavor(self): + flavor_id = CONF.compute.flavor_ref + flavor = self.flavors_client.show_flavor(flavor_id)['flavor'] + flavor['properties'] = self.flavors_client.list_flavor_extra_specs( + flavor_id)['extra_specs'] + return flavor + + def get_rule_path(self, rule_file): + base_path = os.path.split( + os.path.dirname(os.path.abspath(__file__)))[0] + base_path = os.path.split(base_path)[0] + return os.path.join(base_path, "inspector_tempest_plugin", + "rules", rule_file) + + # TODO(aarefiev): switch to call_until_true + def wait_for_introspection_finished(self, node_ids): + """Waits for introspection of baremetal nodes to finish. + + """ + start = int(time.time()) + not_introspected = {node_id for node_id in node_ids} + + while not_introspected: + time.sleep(CONF.baremetal_introspection.introspection_sleep) + for node_id in node_ids: + status = self.introspection_status(node_id) + if status['finished']: + if status['error']: + message = ('Node %(node_id)s introspection failed ' + 'with %(error)s.' % + {'node_id': node_id, + 'error': status['error']}) + raise exceptions.IntrospectionFailed(message) + not_introspected = not_introspected - {node_id} + + if (int(time.time()) - start >= + CONF.baremetal_introspection.introspection_timeout): + message = ('Introspection timed out for nodes: %s' % + not_introspected) + raise exceptions.IntrospectionTimeout(message) + + def wait_for_nova_aware_of_bvms(self): + start = int(time.time()) + while True: + time.sleep(CONF.baremetal_introspection.hypervisor_update_sleep) + stats = self.hypervisor_stats() + expected_cpus = self.baremetal_flavor()['vcpus'] + if int(stats['hypervisor_statistics']['vcpus']) >= expected_cpus: + break + + timeout = CONF.baremetal_introspection.hypervisor_update_timeout + if (int(time.time()) - start >= timeout): + message = ( + 'Timeout while waiting for nova hypervisor-stats: ' + '%(stats)s required time (%(timeout)s s).' % + {'stats': stats, + 'timeout': timeout}) + raise exceptions.HypervisorUpdateTimeout(message) diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py new file mode 100644 index 00000000..6830b78a --- /dev/null +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -0,0 +1,149 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tempest + +from tempest.config import CONF +from tempest import test # noqa + +from ironic_inspector.test.inspector_tempest_plugin.tests import manager +from ironic_tempest_plugin.tests.api.admin.api_microversion_fixture import \ + APIMicroversionFixture as IronicMicroversionFixture +from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ + BaremetalProvisionStates +from tempest.lib.common.api_version_utils import LATEST_MICROVERSION + + +class InspectorBasicTest(manager.InspectorScenarioTest): + wait_provisioning_state_interval = 15 + + def node_cleanup(self, node_id): + if (self.node_show(node_id)['provision_state'] == + BaremetalProvisionStates.AVAILABLE): + return + try: + self.baremetal_client.set_node_provision_state(node_id, 'provide') + except tempest.lib.exceptions.RestClientException: + # maybe node already cleaning or available + pass + + self.wait_provisioning_state( + node_id, [BaremetalProvisionStates.AVAILABLE, + BaremetalProvisionStates.NOSTATE], + timeout=CONF.baremetal.unprovision_timeout, + interval=self.wait_provisioning_state_interval) + + def introspect_node(self, node_id): + # in case there are properties remove those + patch = {('properties/%s' % key): None for key in + self.node_show(node_id)['properties']} + # reset any previous rule result + patch['extra/rule_success'] = None + self.node_update(node_id, patch) + + self.baremetal_client.set_node_provision_state(node_id, 'manage') + self.baremetal_client.set_node_provision_state(node_id, 'inspect') + self.addCleanup(self.node_cleanup, node_id) + + def setUp(self): + super(InspectorBasicTest, self).setUp() + # we rely on the 'available' provision_state; using latest + # microversion + self.useFixture(IronicMicroversionFixture(LATEST_MICROVERSION)) + # avoid testing nodes that aren't available + self.node_ids = {node['uuid'] for node in + self.node_filter(filter=lambda node: + node['provision_state'] == + BaremetalProvisionStates.AVAILABLE)} + if not self.node_ids: + self.skipTest('no available nodes detected') + self.rule_purge() + + def verify_node_introspection_data(self, node): + self.assertEqual('yes', node['extra']['rule_success']) + data = self.introspection_data(node['uuid']) + self.assertEqual(data['cpu_arch'], + self.flavor['properties']['cpu_arch']) + self.assertEqual(int(data['memory_mb']), + int(self.flavor['ram'])) + self.assertEqual(int(data['cpus']), int(self.flavor['vcpus'])) + + def verify_node_flavor(self, node): + expected_cpus = self.flavor['vcpus'] + expected_memory_mb = self.flavor['ram'] + expected_cpu_arch = self.flavor['properties']['cpu_arch'] + disk_size = self.flavor['disk'] + ephemeral_size = self.flavor['OS-FLV-EXT-DATA:ephemeral'] + expected_local_gb = disk_size + ephemeral_size + + self.assertEqual(expected_cpus, + int(node['properties']['cpus'])) + self.assertEqual(expected_memory_mb, + int(node['properties']['memory_mb'])) + self.assertEqual(expected_local_gb, + int(node['properties']['local_gb'])) + self.assertEqual(expected_cpu_arch, + node['properties']['cpu_arch']) + + @test.idempotent_id('03bf7990-bee0-4dd7-bf74-b97ad7b52a4b') + @test.services('baremetal', 'compute', 'image', + 'network', 'object_storage') + def test_baremetal_introspection(self): + """This smoke test case follows this basic set of operations: + + * Fetches expected properties from baremetal flavor + * Removes all properties from nodes + * Sets nodes to manageable state + * Imports introspection rule basic_ops_rule.json + * Inspects nodes + * Verifies all properties are inspected + * Verifies introspection data + * Sets node to available state + * Creates a keypair + * Boots an instance using the keypair + * Deletes the instance + + """ + # prepare introspection rule + rule_path = self.get_rule_path("basic_ops_rule.json") + self.rule_import(rule_path) + self.addCleanup(self.rule_purge) + + for node_id in self.node_ids: + self.introspect_node(node_id) + + # settle down introspection + self.wait_for_introspection_finished(self.node_ids) + for node_id in self.node_ids: + self.wait_provisioning_state( + node_id, 'manageable', + timeout=CONF.baremetal_introspection.ironic_sync_timeout, + interval=self.wait_provisioning_state_interval) + + for node_id in self.node_ids: + node = self.node_show(node_id) + self.verify_node_introspection_data(node) + self.verify_node_flavor(node) + + for node_id in self.node_ids: + self.baremetal_client.set_node_provision_state(node_id, 'provide') + + for node_id in self.node_ids: + self.wait_provisioning_state( + node_id, BaremetalProvisionStates.AVAILABLE, + timeout=CONF.baremetal.active_timeout, + interval=self.wait_provisioning_state_interval) + + self.wait_for_nova_aware_of_bvms() + self.add_keypair() + self.boot_instance() + self.terminate_instance() From d5998c08db4fb354bbd87567b0ab5d300720884f Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Thu, 9 Jun 2016 17:57:09 +0300 Subject: [PATCH 02/26] Tempest: don't rely on tempest ironic client Ironic client will be removed from tempest in near future, switch on ironic tempest plugin. Change-Id: Ifd49503f0b69a67155c2576f9ae70a17f0e01058 --- ironic_tempest_plugin/services/introspection_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic_tempest_plugin/services/introspection_client.py b/ironic_tempest_plugin/services/introspection_client.py index 346e06c6..3f43bf54 100644 --- a/ironic_tempest_plugin/services/introspection_client.py +++ b/ironic_tempest_plugin/services/introspection_client.py @@ -12,10 +12,10 @@ import json +from ironic_tempest_plugin.services.baremetal import base from tempest import clients from tempest.common import credentials_factory as common_creds from tempest import config -from tempest.services.baremetal import base CONF = config.CONF From a17db0239e5033f1585c93b488f858c315a93b5c Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Fri, 17 Jun 2016 11:50:40 +0300 Subject: [PATCH 03/26] Fix tempest tests Base TempestException was removed from tempest, now exceptions based on tempest.lib TempestException, inherited from restclient exceptions. So inherit inspector tempest exceptions from last one. Change-Id: I8058a964e837dbb4aa5a8b214f216453a18a1713 --- ironic_tempest_plugin/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic_tempest_plugin/exceptions.py b/ironic_tempest_plugin/exceptions.py index 7791c40f..ac08d54a 100644 --- a/ironic_tempest_plugin/exceptions.py +++ b/ironic_tempest_plugin/exceptions.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. -from tempest import exceptions +from tempest.lib import exceptions class IntrospectionFailed(exceptions.TempestException): From 84a09c626bd8014829e116450c76856b8f9a0e8c Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Tue, 19 Jul 2016 19:35:18 +0300 Subject: [PATCH 04/26] Tempest: wrap instance actions into inspector methods This commit I4fe31ecae3393abc2779a5e80e348899f9113f1b broke inspector tempest tests, it changes boot_instance and terminate_instance signature. This change redefine action methods Change-Id: If6a9b300bd22e7b62b7e53763cb0328ad30f11c7 --- ironic_tempest_plugin/tests/manager.py | 6 ++++++ ironic_tempest_plugin/tests/test_basic.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index dd2a18eb..b62473ca 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -94,6 +94,12 @@ class InspectorScenarioTest(BaremetalScenarioTest): return os.path.join(base_path, "inspector_tempest_plugin", "rules", rule_file) + def boot_instance(self): + return super(InspectorScenarioTest, self).boot_instance() + + def terminate_instance(self, instance): + return super(InspectorScenarioTest, self).terminate_instance(instance) + # TODO(aarefiev): switch to call_until_true def wait_for_introspection_finished(self, node_ids): """Waits for introspection of baremetal nodes to finish. diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index 6830b78a..0e11cf56 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -145,5 +145,5 @@ class InspectorBasicTest(manager.InspectorScenarioTest): self.wait_for_nova_aware_of_bvms() self.add_keypair() - self.boot_instance() - self.terminate_instance() + ins, _node = self.boot_instance() + self.terminate_instance(ins) From 290c96bb9ca51f16f198365a4937efb3ea1c330d Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Fri, 1 Jul 2016 14:22:51 +0200 Subject: [PATCH 05/26] Add a simple smoke test to be run in the grenade gate This test only runs introspection on one node, nothing else. Also make sure tempest gets our and ironic plugin. Depends-On: Ia2a5b9cc535c7c46728eee6284a36340745e9043 Change-Id: Id12b6cc75977c32f0a9e1ada8ff954b8f4bc2e41 --- ironic_tempest_plugin/tests/manager.py | 46 +++++++++++++ ironic_tempest_plugin/tests/test_basic.py | 81 +++++++++-------------- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index b62473ca..6d0f7d2b 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -14,11 +14,17 @@ import os import time +import tempest from tempest import config +from tempest.lib.common.api_version_utils import LATEST_MICROVERSION from ironic_inspector.test.inspector_tempest_plugin import exceptions from ironic_inspector.test.inspector_tempest_plugin.services import \ introspection_client +from ironic_tempest_plugin.tests.api.admin.api_microversion_fixture import \ + APIMicroversionFixture as IronicMicroversionFixture +from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ + BaremetalProvisionStates from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ BaremetalScenarioTest @@ -29,8 +35,12 @@ CONF = config.CONF class InspectorScenarioTest(BaremetalScenarioTest): """Provide harness to do Inspector scenario tests.""" + wait_provisioning_state_interval = 15 + credentials = ['primary', 'admin'] + ironic_api_version = LATEST_MICROVERSION + @classmethod def setup_clients(cls): super(InspectorScenarioTest, cls).setup_clients() @@ -39,7 +49,15 @@ class InspectorScenarioTest(BaremetalScenarioTest): def setUp(self): super(InspectorScenarioTest, self).setUp() + # we rely on the 'available' provision_state; using latest + # microversion + self.useFixture(IronicMicroversionFixture(self.ironic_api_version)) self.flavor = self.baremetal_flavor() + self.node_ids = {node['uuid'] for node in + self.node_filter(filter=lambda node: + node['provision_state'] == + BaremetalProvisionStates.AVAILABLE)} + self.rule_purge() def item_filter(self, list_method, show_method, filter=lambda item: True, items=None): @@ -144,3 +162,31 @@ class InspectorScenarioTest(BaremetalScenarioTest): {'stats': stats, 'timeout': timeout}) raise exceptions.HypervisorUpdateTimeout(message) + + def node_cleanup(self, node_id): + if (self.node_show(node_id)['provision_state'] == + BaremetalProvisionStates.AVAILABLE): + return + try: + self.baremetal_client.set_node_provision_state(node_id, 'provide') + except tempest.lib.exceptions.RestClientException: + # maybe node already cleaning or available + pass + + self.wait_provisioning_state( + node_id, [BaremetalProvisionStates.AVAILABLE, + BaremetalProvisionStates.NOSTATE], + timeout=CONF.baremetal.unprovision_timeout, + interval=self.wait_provisioning_state_interval) + + def introspect_node(self, node_id): + # in case there are properties remove those + patch = {('properties/%s' % key): None for key in + self.node_show(node_id)['properties']} + # reset any previous rule result + patch['extra/rule_success'] = None + self.node_update(node_id, patch) + + self.baremetal_client.set_node_provision_state(node_id, 'manage') + self.baremetal_client.set_node_provision_state(node_id, 'inspect') + self.addCleanup(self.node_cleanup, node_id) diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index 0e11cf56..053bc9f8 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -10,63 +10,15 @@ # License for the specific language governing permissions and limitations # under the License. -import tempest - from tempest.config import CONF from tempest import test # noqa from ironic_inspector.test.inspector_tempest_plugin.tests import manager -from ironic_tempest_plugin.tests.api.admin.api_microversion_fixture import \ - APIMicroversionFixture as IronicMicroversionFixture from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ BaremetalProvisionStates -from tempest.lib.common.api_version_utils import LATEST_MICROVERSION class InspectorBasicTest(manager.InspectorScenarioTest): - wait_provisioning_state_interval = 15 - - def node_cleanup(self, node_id): - if (self.node_show(node_id)['provision_state'] == - BaremetalProvisionStates.AVAILABLE): - return - try: - self.baremetal_client.set_node_provision_state(node_id, 'provide') - except tempest.lib.exceptions.RestClientException: - # maybe node already cleaning or available - pass - - self.wait_provisioning_state( - node_id, [BaremetalProvisionStates.AVAILABLE, - BaremetalProvisionStates.NOSTATE], - timeout=CONF.baremetal.unprovision_timeout, - interval=self.wait_provisioning_state_interval) - - def introspect_node(self, node_id): - # in case there are properties remove those - patch = {('properties/%s' % key): None for key in - self.node_show(node_id)['properties']} - # reset any previous rule result - patch['extra/rule_success'] = None - self.node_update(node_id, patch) - - self.baremetal_client.set_node_provision_state(node_id, 'manage') - self.baremetal_client.set_node_provision_state(node_id, 'inspect') - self.addCleanup(self.node_cleanup, node_id) - - def setUp(self): - super(InspectorBasicTest, self).setUp() - # we rely on the 'available' provision_state; using latest - # microversion - self.useFixture(IronicMicroversionFixture(LATEST_MICROVERSION)) - # avoid testing nodes that aren't available - self.node_ids = {node['uuid'] for node in - self.node_filter(filter=lambda node: - node['provision_state'] == - BaremetalProvisionStates.AVAILABLE)} - if not self.node_ids: - self.skipTest('no available nodes detected') - self.rule_purge() def verify_node_introspection_data(self, node): self.assertEqual('yes', node['extra']['rule_success']) @@ -98,7 +50,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): @test.services('baremetal', 'compute', 'image', 'network', 'object_storage') def test_baremetal_introspection(self): - """This smoke test case follows this basic set of operations: + """This smoke test case follows this set of operations: * Fetches expected properties from baremetal flavor * Removes all properties from nodes @@ -147,3 +99,34 @@ class InspectorBasicTest(manager.InspectorScenarioTest): self.add_keypair() ins, _node = self.boot_instance() self.terminate_instance(ins) + + +class InspectorSmokeTest(manager.InspectorScenarioTest): + + @test.idempotent_id('a702d1f1-88e4-42ce-88ef-cba2d9e3312e') + @test.attr(type='smoke') + @test.services('baremetal', 'compute', 'image', + 'network', 'object_storage') + def test_baremetal_introspection(self): + """This smoke test case follows this very basic set of operations: + + * Fetches expected properties from baremetal flavor + * Removes all properties from one node + * Sets the node to manageable state + * Inspects the node + * Sets the node to available state + + """ + # NOTE(dtantsur): we can't silently skip this test because it runs in + # grenade with several other tests, and we won't have any indication + # that it was not run. + assert self.node_ids, "No available nodes" + node_id = next(iter(self.node_ids)) + self.introspect_node(node_id) + + # settle down introspection + self.wait_for_introspection_finished([node_id]) + self.wait_provisioning_state( + node_id, 'manageable', + timeout=CONF.baremetal_introspection.ironic_sync_timeout, + interval=self.wait_provisioning_state_interval) From 6dc8279cc5cedb22d3a7e56ee5b97fc6aa161f0a Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Wed, 3 Aug 2016 11:50:49 +0300 Subject: [PATCH 06/26] Tempest: increase ironic sync timeout Increase ironic_sync_timeout to 80 sec. Ironic default status check period is 60 sec. But calling Ironic API to get node status takes some time, and races appear, as result tempest job periodically fails. 80 sec would be more than enough to make one more check. Change-Id: I5fe0198b2ce2f1f0078c1d14aa1c367a71178097 --- ironic_tempest_plugin/config.py | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 ironic_tempest_plugin/config.py diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py new file mode 100644 index 00000000..98029345 --- /dev/null +++ b/ironic_tempest_plugin/config.py @@ -0,0 +1,55 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_config import cfg + +from tempest import config # noqa + + +baremetal_introspection_group = cfg.OptGroup( + name="baremetal_introspection", + title="Baremetal introspection service options", + help="When enabling baremetal introspection tests," + "Ironic must be configured.") + +BaremetalIntrospectionGroup = [ + cfg.StrOpt('catalog_type', + default='baremetal-introspection', + help="Catalog type of the baremetal provisioning service"), + cfg.StrOpt('endpoint_type', + default='publicURL', + choices=['public', 'admin', 'internal', + 'publicURL', 'adminURL', 'internalURL'], + help="The endpoint type to use for the baremetal introspection" + " service"), + cfg.IntOpt('introspection_sleep', + default=30, + help="Introspection sleep before check status"), + cfg.IntOpt('introspection_timeout', + default=600, + help="Introspection time out"), + cfg.IntOpt('hypervisor_update_sleep', + default=60, + help="Time to wait until nova becomes aware of " + "bare metal instances"), + cfg.IntOpt('hypervisor_update_timeout', + default=300, + help="Time out for wait until nova becomes aware of " + "bare metal instances"), + # NOTE(aarefiev): status_check_period default is 60s, but checking + # node state takes some time(API call), so races appear here, + # 80s would be enough to make one more check. + cfg.IntOpt('ironic_sync_timeout', + default=80, + help="Time it might take for Ironic--Inspector " + "sync to happen"), +] From 8a7942e10544cb01869c89eb3bf3a6623e514d1d Mon Sep 17 00:00:00 2001 From: AvnishPal Date: Tue, 23 Aug 2016 16:11:44 +0530 Subject: [PATCH 07/26] Fix tempest.conf generation [service_available] is not being generated. This patch fixes it. Change-Id: I364621ce96a1d2f6bc49df6f2868b093f719a6f5 Closes-Bug: #1613542 --- ironic_tempest_plugin/config.py | 9 +++++++ ironic_tempest_plugin/plugin.py | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 ironic_tempest_plugin/plugin.py diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py index 98029345..7b09e903 100644 --- a/ironic_tempest_plugin/config.py +++ b/ironic_tempest_plugin/config.py @@ -14,6 +14,15 @@ from oslo_config import cfg from tempest import config # noqa +service_available_group = cfg.OptGroup(name="service_available", + title="Available OpenStack Services") + +ServiceAvailableGroup = [ + cfg.BoolOpt("ironic-inspector", + default=True, + help="Whether or not ironic-inspector is expected to be" + " available"), +] baremetal_introspection_group = cfg.OptGroup( name="baremetal_introspection", diff --git a/ironic_tempest_plugin/plugin.py b/ironic_tempest_plugin/plugin.py new file mode 100644 index 00000000..d0601653 --- /dev/null +++ b/ironic_tempest_plugin/plugin.py @@ -0,0 +1,43 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import os + +from tempest import config as tempest_config +from tempest.test_discover import plugins + +from ironic_inspector.test.inspector_tempest_plugin import config + + +class InspectorTempestPlugin(plugins.TempestPlugin): + def load_tests(self): + base_path = os.path.split(os.path.dirname( + os.path.abspath(__file__)))[0] + test_dir = "inspector_tempest_plugin/tests" + full_test_dir = os.path.join(base_path, test_dir) + return full_test_dir, base_path + + def register_opts(self, conf): + tempest_config.register_opt_group( + conf, config.service_available_group, + config.ServiceAvailableGroup) + tempest_config.register_opt_group( + conf, config.baremetal_introspection_group, + config.BaremetalIntrospectionGroup) + + def get_opt_lists(self): + return [ + (config.baremetal_introspection_group.name, + config.BaremetalIntrospectionGroup), + ('service_available', config.ServiceAvailableGroup) + ] From b317e11a4334082a41ae10d230a0b3d3702e3df1 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Fri, 9 Sep 2016 11:03:27 +0200 Subject: [PATCH 08/26] Disable neutron tests in our grenade We're suffering from unclear failures test_network_basic_ops. As these tests are not important for our service, disabling them for now by pretending that Neutron is not available. Also fix incorrect list of required services on our smoke test, it only requires Ironic and Swift. Change-Id: Ia0f0976c2516e853482277a1a1045b4a951dec7c Partial-Bug: #1621791 --- ironic_tempest_plugin/plugin.py | 4 ++++ ironic_tempest_plugin/tests/test_basic.py | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ironic_tempest_plugin/plugin.py b/ironic_tempest_plugin/plugin.py index d0601653..c411cc8f 100644 --- a/ironic_tempest_plugin/plugin.py +++ b/ironic_tempest_plugin/plugin.py @@ -13,6 +13,7 @@ import os +from oslo_config import cfg from tempest import config as tempest_config from tempest.test_discover import plugins @@ -34,6 +35,9 @@ class InspectorTempestPlugin(plugins.TempestPlugin): tempest_config.register_opt_group( conf, config.baremetal_introspection_group, config.BaremetalIntrospectionGroup) + # FIXME(dtantsur): pretend like Neutron does not exist due to random + # failures, see https://bugs.launchpad.net/bugs/1621791. + cfg.CONF.set_override('neutron', False, 'service_available') def get_opt_lists(self): return [ diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index 053bc9f8..6ef2266a 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -105,8 +105,7 @@ class InspectorSmokeTest(manager.InspectorScenarioTest): @test.idempotent_id('a702d1f1-88e4-42ce-88ef-cba2d9e3312e') @test.attr(type='smoke') - @test.services('baremetal', 'compute', 'image', - 'network', 'object_storage') + @test.services('baremetal', 'object_storage') def test_baremetal_introspection(self): """This smoke test case follows this very basic set of operations: From 6b0035651ec6feee7e2db8d7ef5e3bab672f9c76 Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Tue, 13 Sep 2016 12:17:29 +0300 Subject: [PATCH 09/26] Tempest: add auto-discovery test Add test, which delete pre-created baremetal vms, and discovers it via 'enroll' not_found_hook with default configuration. Note, test contains workaround for working on infra, as infra 'tempest' user doesn't have access to virsh, for running node and whitelisting firewall rules on existing node, inspector's inspect api is used. Change-Id: Ib0ec63295a496229b27552cd1bcf7e763c0c3e03 --- ironic_tempest_plugin/config.py | 9 ++ .../services/introspection_client.py | 23 +-- ironic_tempest_plugin/tests/manager.py | 47 +++++- ironic_tempest_plugin/tests/test_discovery.py | 147 ++++++++++++++++++ 4 files changed, 215 insertions(+), 11 deletions(-) create mode 100644 ironic_tempest_plugin/tests/test_discovery.py diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py index 7b09e903..00540cf0 100644 --- a/ironic_tempest_plugin/config.py +++ b/ironic_tempest_plugin/config.py @@ -61,4 +61,13 @@ BaremetalIntrospectionGroup = [ default=80, help="Time it might take for Ironic--Inspector " "sync to happen"), + cfg.IntOpt('discovery_timeout', + default=300, + help="Time to wait until new node would enrolled in " + "ironic"), + cfg.BoolOpt('auto_discovery_feature', + default=False, + help="Is the auto-discovery feature enabled. Enroll hook " + "should be specified in node_not_found_hook - processing " + "section of inspector.conf"), ] diff --git a/ironic_tempest_plugin/services/introspection_client.py b/ironic_tempest_plugin/services/introspection_client.py index 3f43bf54..cce52130 100644 --- a/ironic_tempest_plugin/services/introspection_client.py +++ b/ironic_tempest_plugin/services/introspection_client.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import json - from ironic_tempest_plugin.services.baremetal import base from tempest import clients from tempest.common import credentials_factory as common_creds @@ -47,13 +45,10 @@ class BaremetalIntrospectionClient(base.BaremetalClient): return self._delete_request('rules', uuid=None) @base.handle_errors - def import_rule(self, rule_path): - """Import introspection rules from a json file.""" - with open(rule_path, 'r') as fp: - rules = json.load(fp) - if not isinstance(rules, list): - rules = [rules] - + def create_rules(self, rules): + """Create introspection rules.""" + if not isinstance(rules, list): + rules = [rules] for rule in rules: self._create_request('rules', rule) @@ -68,3 +63,13 @@ class BaremetalIntrospectionClient(base.BaremetalClient): return self._show_request('introspection', uuid=uuid, uri='/%s/introspection/%s/data' % (self.uri_prefix, uuid)) + + @base.handle_errors + def start_introspection(self, uuid): + """Start introspection for a node.""" + resp, _body = self.post(url=('/%s/introspection/%s' % + (self.uri_prefix, uuid)), + body=None) + self.expected_success(202, resp.status) + + return resp diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index 6d0f7d2b..445a15e0 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -10,13 +10,16 @@ # License for the specific language governing permissions and limitations # under the License. - +import json import os +import six import time import tempest from tempest import config from tempest.lib.common.api_version_utils import LATEST_MICROVERSION +from tempest.lib import exceptions as lib_exc +from tempest import test from ironic_inspector.test.inspector_tempest_plugin import exceptions from ironic_inspector.test.inspector_tempest_plugin.services import \ @@ -69,16 +72,28 @@ class InspectorScenarioTest(BaremetalScenarioTest): def node_list(self): return self.baremetal_client.list_nodes()[1]['nodes'] + def node_port_list(self, node_uuid): + return self.baremetal_client.list_node_ports(node_uuid)[1]['ports'] + def node_update(self, uuid, patch): return self.baremetal_client.update_node(uuid, **patch) def node_show(self, uuid): return self.baremetal_client.show_node(uuid)[1] + def node_delete(self, uuid): + return self.baremetal_client.delete_node(uuid) + def node_filter(self, filter=lambda node: True, nodes=None): return self.item_filter(self.node_list, self.node_show, filter=filter, items=nodes) + def node_set_power_state(self, uuid, state): + self.baremetal_client.set_node_power_state(uuid, state) + + def node_set_provision_state(self, uuid, state): + self.baremetal_client.set_node_provision_state(self, uuid, state) + def hypervisor_stats(self): return (self.admin_manager.hypervisor_client. show_hypervisor_statistics()) @@ -90,7 +105,12 @@ class InspectorScenarioTest(BaremetalScenarioTest): self.introspection_client.purge_rules() def rule_import(self, rule_path): - self.introspection_client.import_rule(rule_path) + with open(rule_path, 'r') as fp: + rules = json.load(fp) + self.introspection_client.create_rules(rules) + + def rule_import_from_dict(self, rules): + self.introspection_client.create_rules(rules) def introspection_status(self, uuid): return self.introspection_client.get_status(uuid)[1] @@ -98,6 +118,9 @@ class InspectorScenarioTest(BaremetalScenarioTest): def introspection_data(self, uuid): return self.introspection_client.get_data(uuid)[1] + def introspection_start(self, uuid): + return self.introspection_client.start_introspection(uuid) + def baremetal_flavor(self): flavor_id = CONF.compute.flavor_ref flavor = self.flavors_client.show_flavor(flavor_id)['flavor'] @@ -118,11 +141,31 @@ class InspectorScenarioTest(BaremetalScenarioTest): def terminate_instance(self, instance): return super(InspectorScenarioTest, self).terminate_instance(instance) + def wait_for_node(self, node_name): + def check_node(): + try: + self.node_show(node_name) + except lib_exc.NotFound: + return False + return True + + if not test.call_until_true( + check_node, + duration=CONF.baremetal_introspection.discovery_timeout, + sleep_for=20): + msg = ("Timed out waiting for node %s " % node_name) + raise lib_exc.TimeoutException(msg) + + inspected_node = self.node_show(self.node_info['name']) + self.wait_for_introspection_finished(inspected_node['uuid']) + # TODO(aarefiev): switch to call_until_true def wait_for_introspection_finished(self, node_ids): """Waits for introspection of baremetal nodes to finish. """ + if isinstance(node_ids, six.text_type): + node_ids = [node_ids] start = int(time.time()) not_introspected = {node_id for node_id in node_ids} diff --git a/ironic_tempest_plugin/tests/test_discovery.py b/ironic_tempest_plugin/tests/test_discovery.py new file mode 100644 index 00000000..592fa816 --- /dev/null +++ b/ironic_tempest_plugin/tests/test_discovery.py @@ -0,0 +1,147 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import six + +from ironic_tempest_plugin.tests.scenario import baremetal_manager +from tempest import config +from tempest import test # noqa + +from ironic_inspector.test.inspector_tempest_plugin.tests import manager + +CONF = config.CONF + +ProvisionStates = baremetal_manager.BaremetalProvisionStates + + +class InspectorDiscoveryTest(manager.InspectorScenarioTest): + @classmethod + def skip_checks(cls): + super(InspectorDiscoveryTest, cls).skip_checks() + if not CONF.baremetal_introspection.auto_discovery_feature: + msg = ("Please, provide a value for node_not_found_hook in " + "processing section of inspector.conf for enable " + "auto-discovery feature.") + raise cls.skipException(msg) + + def setUp(self): + super(InspectorDiscoveryTest, self).setUp() + + discovered_node = self._get_discovery_node() + self.node_info = self._get_node_info(discovered_node) + + rule = self._generate_discovery_rule(self.node_info) + + self.rule_import_from_dict(rule) + self.addCleanup(self.rule_purge) + + def _get_node_info(self, node_uuid): + node = self.node_show(node_uuid) + ports = self.node_port_list(node_uuid) + node['port_macs'] = [port['address'] for port in ports] + return node + + def _get_discovery_node(self): + nodes = self.node_list() + + discovered_node = None + for node in nodes: + if (node['provision_state'] == ProvisionStates.AVAILABLE or + node['provision_state'] == ProvisionStates.ENROLL or + node['provision_state'] is ProvisionStates.NOSTATE): + discovered_node = node['uuid'] + break + + self.assertIsNotNone(discovered_node) + return discovered_node + + def _generate_discovery_rule(self, node): + rule = dict() + rule["description"] = "Node %s discovery rule" % node['name'] + rule["actions"] = [ + {"action": "set-attribute", "path": "/name", + "value": "%s" % node['name']}, + {"action": "set-attribute", "path": "/driver", + "value": "%s" % node['driver']}, + ] + + for key, value in node['driver_info'].items(): + rule["actions"].append( + {"action": "set-attribute", "path": "/driver_info/%s" % key, + "value": "%s" % value}) + rule["conditions"] = [ + {"op": "eq", "field": "data://auto_discovered", "value": True} + ] + return rule + + def verify_node_introspection_data(self, node): + data = self.introspection_data(node['uuid']) + self.assertEqual(data['cpu_arch'], + self.flavor['properties']['cpu_arch']) + self.assertEqual(int(data['memory_mb']), + int(self.flavor['ram'])) + self.assertEqual(int(data['cpus']), int(self.flavor['vcpus'])) + + def verify_node_flavor(self, node): + expected_cpus = self.flavor['vcpus'] + expected_memory_mb = self.flavor['ram'] + expected_cpu_arch = self.flavor['properties']['cpu_arch'] + disk_size = self.flavor['disk'] + ephemeral_size = self.flavor['OS-FLV-EXT-DATA:ephemeral'] + expected_local_gb = disk_size + ephemeral_size + + self.assertEqual(expected_cpus, + int(node['properties']['cpus'])) + self.assertEqual(expected_memory_mb, + int(node['properties']['memory_mb'])) + self.assertEqual(expected_local_gb, + int(node['properties']['local_gb'])) + self.assertEqual(expected_cpu_arch, + node['properties']['cpu_arch']) + + def verify_node_driver_info(self, node_info, inspected_node): + for key in node_info['driver_info']: + self.assertEqual(six.text_type(node_info['driver_info'][key]), + inspected_node['driver_info'].get(key)) + + @test.idempotent_id('dd3abe5e-0d23-488d-bb4e-344cdeff7dcb') + @test.services('baremetal', 'compute') + def test_berametal_auto_discovery(self): + """This test case follows this set of operations: + + * Choose appropriate node, based on provision state; + * Get node info; + * Generate discovery rule; + * Delete discovered node from ironic; + * Start baremetal vm via virsh; + * Wating for node introspection; + * Verify introspected node. + """ + # NOTE(aarefiev): workaround for infra, 'tempest' user doesn't + # have virsh privileges, so lets power on the node via ironic + # and then delete it. Because of node is blacklisted in inspector + # we can't just power on it, therefor start introspection is used + # to whitelist discovered node first. + self.baremetal_client.set_node_provision_state( + self.node_info['uuid'], 'manage') + self.introspection_start(self.node_info['uuid']) + self.wait_power_state( + self.node_info['uuid'], + baremetal_manager.BaremetalPowerStates.POWER_ON) + self.node_delete(self.node_info['uuid']) + + self.wait_for_node(self.node_info['name']) + + inspected_node = self.node_show(self.node_info['name']) + self.verify_node_flavor(inspected_node) + self.verify_node_introspection_data(inspected_node) + self.verify_node_driver_info(self.node_info, inspected_node) From 6fcbdd7cf8b97f583a4a5af9345a21869055a638 Mon Sep 17 00:00:00 2001 From: Nishant Kumar Date: Mon, 10 Oct 2016 13:27:10 +0530 Subject: [PATCH 10/26] Stop adding ServiceAvailable group option Service available group already exists.Therefore we don't need to register this group here again. Change-Id: I930e53e4934d72ed76735847581932fd227be2f0 Closes-Bug: #1621036 --- ironic_tempest_plugin/config.py | 15 ++++----------- ironic_tempest_plugin/plugin.py | 14 ++++++-------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py index 00540cf0..e5869003 100644 --- a/ironic_tempest_plugin/config.py +++ b/ironic_tempest_plugin/config.py @@ -12,17 +12,10 @@ from oslo_config import cfg -from tempest import config # noqa - -service_available_group = cfg.OptGroup(name="service_available", - title="Available OpenStack Services") - -ServiceAvailableGroup = [ - cfg.BoolOpt("ironic-inspector", - default=True, - help="Whether or not ironic-inspector is expected to be" - " available"), -] +service_option = cfg.BoolOpt("ironic-inspector", + default=True, + help="Whether or not ironic-inspector is expected" + " to be available") baremetal_introspection_group = cfg.OptGroup( name="baremetal_introspection", diff --git a/ironic_tempest_plugin/plugin.py b/ironic_tempest_plugin/plugin.py index c411cc8f..32a6d372 100644 --- a/ironic_tempest_plugin/plugin.py +++ b/ironic_tempest_plugin/plugin.py @@ -14,7 +14,6 @@ import os from oslo_config import cfg -from tempest import config as tempest_config from tempest.test_discover import plugins from ironic_inspector.test.inspector_tempest_plugin import config @@ -29,12 +28,11 @@ class InspectorTempestPlugin(plugins.TempestPlugin): return full_test_dir, base_path def register_opts(self, conf): - tempest_config.register_opt_group( - conf, config.service_available_group, - config.ServiceAvailableGroup) - tempest_config.register_opt_group( - conf, config.baremetal_introspection_group, - config.BaremetalIntrospectionGroup) + conf.register_opt(config.service_option, + group='service_available') + conf.register_group(config.baremetal_introspection_group) + conf.register_opts(config.BaremetalIntrospectionGroup, + group="baremetal_introspection") # FIXME(dtantsur): pretend like Neutron does not exist due to random # failures, see https://bugs.launchpad.net/bugs/1621791. cfg.CONF.set_override('neutron', False, 'service_available') @@ -43,5 +41,5 @@ class InspectorTempestPlugin(plugins.TempestPlugin): return [ (config.baremetal_introspection_group.name, config.BaremetalIntrospectionGroup), - ('service_available', config.ServiceAvailableGroup) + ('service_available', [config.service_option]) ] From 0de45c46c46d9d90553210fd5e7adc0ed15fc691 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 17 Oct 2016 14:31:40 +0200 Subject: [PATCH 11/26] Only disable Neutron tests when our Grenade is running Unconditionally disabling it in code disables neutron for everyone importing our plugin. Change-Id: I468af14f42b6d9227179e921f5a3ccea2cae0d66 Partial-Bug: #1621791 --- ironic_tempest_plugin/plugin.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ironic_tempest_plugin/plugin.py b/ironic_tempest_plugin/plugin.py index 32a6d372..218f1001 100644 --- a/ironic_tempest_plugin/plugin.py +++ b/ironic_tempest_plugin/plugin.py @@ -33,9 +33,10 @@ class InspectorTempestPlugin(plugins.TempestPlugin): conf.register_group(config.baremetal_introspection_group) conf.register_opts(config.BaremetalIntrospectionGroup, group="baremetal_introspection") - # FIXME(dtantsur): pretend like Neutron does not exist due to random - # failures, see https://bugs.launchpad.net/bugs/1621791. - cfg.CONF.set_override('neutron', False, 'service_available') + if os.path.exists('/tmp/ironic-inspector-grenade'): + # FIXME(dtantsur): pretend like Neutron does not exist due to + # random failures, see https://bugs.launchpad.net/bugs/1621791. + cfg.CONF.set_override('neutron', False, 'service_available') def get_opt_lists(self): return [ From 1eb13cfe31fede12a842f15af145aa91f048eb05 Mon Sep 17 00:00:00 2001 From: Sergii Nozhka Date: Fri, 4 Nov 2016 17:15:50 +0200 Subject: [PATCH 12/26] Add a test for introspection abort action verification. Change-Id: Ia2c371a862a800220323e81545e6a8693dac51f0 --- .../services/introspection_client.py | 10 ++++ ironic_tempest_plugin/tests/manager.py | 23 ++++++--- ironic_tempest_plugin/tests/test_basic.py | 51 +++++++++++++++++-- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/ironic_tempest_plugin/services/introspection_client.py b/ironic_tempest_plugin/services/introspection_client.py index cce52130..2934e544 100644 --- a/ironic_tempest_plugin/services/introspection_client.py +++ b/ironic_tempest_plugin/services/introspection_client.py @@ -73,3 +73,13 @@ class BaremetalIntrospectionClient(base.BaremetalClient): self.expected_success(202, resp.status) return resp + + @base.handle_errors + def abort_introspection(self, uuid): + """Abort introspection for a node.""" + resp, _body = self.post(url=('/%s/introspection/%s/abort' % + (self.uri_prefix, uuid)), + body=None) + self.expected_success(202, resp.status) + + return resp diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index 445a15e0..961b2847 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -121,6 +121,9 @@ class InspectorScenarioTest(BaremetalScenarioTest): def introspection_start(self, uuid): return self.introspection_client.start_introspection(uuid) + def introspection_abort(self, uuid): + return self.introspection_client.abort_introspection(uuid) + def baremetal_flavor(self): flavor_id = CONF.compute.flavor_ref flavor = self.flavors_client.show_flavor(flavor_id)['flavor'] @@ -210,6 +213,11 @@ class InspectorScenarioTest(BaremetalScenarioTest): if (self.node_show(node_id)['provision_state'] == BaremetalProvisionStates.AVAILABLE): return + # in case when introspection failed we need set provision state + # to 'manage' to make it possible transit into 'provide' state + if self.node_show(node_id)['provision_state'] == 'inspect failed': + self.baremetal_client.set_node_provision_state(node_id, 'manage') + try: self.baremetal_client.set_node_provision_state(node_id, 'provide') except tempest.lib.exceptions.RestClientException: @@ -222,13 +230,14 @@ class InspectorScenarioTest(BaremetalScenarioTest): timeout=CONF.baremetal.unprovision_timeout, interval=self.wait_provisioning_state_interval) - def introspect_node(self, node_id): - # in case there are properties remove those - patch = {('properties/%s' % key): None for key in - self.node_show(node_id)['properties']} - # reset any previous rule result - patch['extra/rule_success'] = None - self.node_update(node_id, patch) + def introspect_node(self, node_id, remove_props=True): + if remove_props: + # in case there are properties remove those + patch = {('properties/%s' % key): None for key in + self.node_show(node_id)['properties']} + # reset any previous rule result + patch['extra/rule_success'] = None + self.node_update(node_id, patch) self.baremetal_client.set_node_provision_state(node_id, 'manage') self.baremetal_client.set_node_provision_state(node_id, 'inspect') diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index 6ef2266a..dd099c0a 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -14,8 +14,7 @@ from tempest.config import CONF from tempest import test # noqa from ironic_inspector.test.inspector_tempest_plugin.tests import manager -from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ - BaremetalProvisionStates +from ironic_tempest_plugin.tests.scenario import baremetal_manager class InspectorBasicTest(manager.InspectorScenarioTest): @@ -46,6 +45,17 @@ class InspectorBasicTest(manager.InspectorScenarioTest): self.assertEqual(expected_cpu_arch, node['properties']['cpu_arch']) + def verify_introspection_aborted(self, uuid): + status = self.introspection_status(uuid) + + self.assertEqual('Canceled by operator', status['error']) + self.assertTrue(status['finished']) + + self.wait_provisioning_state( + uuid, 'inspect failed', + timeout=CONF.baremetal.active_timeout, + interval=self.wait_provisioning_state_interval) + @test.idempotent_id('03bf7990-bee0-4dd7-bf74-b97ad7b52a4b') @test.services('baremetal', 'compute', 'image', 'network', 'object_storage') @@ -91,7 +101,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): for node_id in self.node_ids: self.wait_provisioning_state( - node_id, BaremetalProvisionStates.AVAILABLE, + node_id, baremetal_manager.BaremetalProvisionStates.AVAILABLE, timeout=CONF.baremetal.active_timeout, interval=self.wait_provisioning_state_interval) @@ -100,6 +110,41 @@ class InspectorBasicTest(manager.InspectorScenarioTest): ins, _node = self.boot_instance() self.terminate_instance(ins) + @test.idempotent_id('70ca3070-184b-4b7d-8892-e977d2bc2870') + @test.services('baremetal') + def test_introspection_abort(self): + """This smoke test case follows this very basic set of operations: + + * Start nodes introspection + * Wait until nodes power on + * Abort introspection + * Verifies nodes status and power state + + """ + # start nodes introspection + for node_id in self.node_ids: + self.introspect_node(node_id, remove_props=False) + + # wait for nodes power on + for node_id in self.node_ids: + self.wait_power_state( + node_id, + baremetal_manager.BaremetalPowerStates.POWER_ON) + + # abort introspection + for node_id in self.node_ids: + self.introspection_abort(node_id) + + # wait for nodes power off + for node_id in self.node_ids: + self.wait_power_state( + node_id, + baremetal_manager.BaremetalPowerStates.POWER_OFF) + + # verify nodes status and provision state + for node_id in self.node_ids: + self.verify_introspection_aborted(node_id) + class InspectorSmokeTest(manager.InspectorScenarioTest): From fc2e62b9383ec9ebe88d4b0be6126e4a4c26d6c0 Mon Sep 17 00:00:00 2001 From: Mario Villaplana Date: Wed, 16 Nov 2016 19:12:40 +0000 Subject: [PATCH 13/26] Test discovered nodes are in ENROLL state and fix typo This adds additional coverage to the discovery tempest test to check that discovered nodes start in the ENROLL state. A small typo is also corrected. Change-Id: I7b3ad1ec4d8779f4c4e58776280d955a1b061fb7 --- ironic_tempest_plugin/tests/test_discovery.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ironic_tempest_plugin/tests/test_discovery.py b/ironic_tempest_plugin/tests/test_discovery.py index 592fa816..c4508b7d 100644 --- a/ironic_tempest_plugin/tests/test_discovery.py +++ b/ironic_tempest_plugin/tests/test_discovery.py @@ -115,7 +115,7 @@ class InspectorDiscoveryTest(manager.InspectorScenarioTest): @test.idempotent_id('dd3abe5e-0d23-488d-bb4e-344cdeff7dcb') @test.services('baremetal', 'compute') - def test_berametal_auto_discovery(self): + def test_bearmetal_auto_discovery(self): """This test case follows this set of operations: * Choose appropriate node, based on provision state; @@ -145,3 +145,5 @@ class InspectorDiscoveryTest(manager.InspectorScenarioTest): self.verify_node_flavor(inspected_node) self.verify_node_introspection_data(inspected_node) self.verify_node_driver_info(self.node_info, inspected_node) + self.assertEqual(ProvisionStates.ENROLL, + inspected_node['provision_state']) From 3038589d525915af8aab46aed338782373c72175 Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Fri, 18 Nov 2016 11:48:07 +0200 Subject: [PATCH 14/26] Stop disabling the Neutron tempest plugin The root cause for the Inspector grenade failures was an eventlet monkey patch[1]. The issue now being solved, disabling the Neutron tempest plugin is no longer necessary. [1] https://review.openstack.org/#/c/399469/ Closes-bug: #1621791 Change-Id: Icaca019880054e1d89b81736c2815a222e8abc45 --- ironic_tempest_plugin/plugin.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ironic_tempest_plugin/plugin.py b/ironic_tempest_plugin/plugin.py index 218f1001..0428c7d2 100644 --- a/ironic_tempest_plugin/plugin.py +++ b/ironic_tempest_plugin/plugin.py @@ -13,7 +13,6 @@ import os -from oslo_config import cfg from tempest.test_discover import plugins from ironic_inspector.test.inspector_tempest_plugin import config @@ -33,10 +32,6 @@ class InspectorTempestPlugin(plugins.TempestPlugin): conf.register_group(config.baremetal_introspection_group) conf.register_opts(config.BaremetalIntrospectionGroup, group="baremetal_introspection") - if os.path.exists('/tmp/ironic-inspector-grenade'): - # FIXME(dtantsur): pretend like Neutron does not exist due to - # random failures, see https://bugs.launchpad.net/bugs/1621791. - cfg.CONF.set_override('neutron', False, 'service_available') def get_opt_lists(self): return [ From 1611df8ba952b7974c763c163936aa5a36339e81 Mon Sep 17 00:00:00 2001 From: Jim Rollenhagen Date: Wed, 7 Dec 2016 10:31:43 -0500 Subject: [PATCH 15/26] Remove default_params_with_timeout_values from tempest client This is being removed from tempest, and was anyway about build timeouts so it isn't being used in inspector. Change-Id: Idb27d13eae20f4b3c1c5d352ca25ec44436c4a56 Related-Bug: #1614516 --- ironic_tempest_plugin/services/introspection_client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ironic_tempest_plugin/services/introspection_client.py b/ironic_tempest_plugin/services/introspection_client.py index 2934e544..5bb03c2e 100644 --- a/ironic_tempest_plugin/services/introspection_client.py +++ b/ironic_tempest_plugin/services/introspection_client.py @@ -30,8 +30,7 @@ class Manager(clients.Manager): self.auth_provider, CONF.baremetal_introspection.catalog_type, CONF.identity.region, - endpoint_type=CONF.baremetal_introspection.endpoint_type, - **self.default_params_with_timeout_values) + endpoint_type=CONF.baremetal_introspection.endpoint_type) class BaremetalIntrospectionClient(base.BaremetalClient): From 63404e2f7666a3a319d420f283e41f82415b42e8 Mon Sep 17 00:00:00 2001 From: dparalen Date: Mon, 19 Dec 2016 14:05:33 +0100 Subject: [PATCH 16/26] Tempest test tag baremetal doesn't exist Remove the tempest baremetal test case tag. Also remove the compute tag where not booting a VM via the Nova service. Change-Id: I2660fda0c5aeca656be5c7b565f2e0bf255658f1 Closes-Bug: 1651123 --- ironic_tempest_plugin/tests/test_basic.py | 6 ++---- ironic_tempest_plugin/tests/test_discovery.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index dd099c0a..7d9cc72d 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -57,8 +57,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): interval=self.wait_provisioning_state_interval) @test.idempotent_id('03bf7990-bee0-4dd7-bf74-b97ad7b52a4b') - @test.services('baremetal', 'compute', 'image', - 'network', 'object_storage') + @test.services('compute', 'image', 'network', 'object_storage') def test_baremetal_introspection(self): """This smoke test case follows this set of operations: @@ -111,7 +110,6 @@ class InspectorBasicTest(manager.InspectorScenarioTest): self.terminate_instance(ins) @test.idempotent_id('70ca3070-184b-4b7d-8892-e977d2bc2870') - @test.services('baremetal') def test_introspection_abort(self): """This smoke test case follows this very basic set of operations: @@ -150,7 +148,7 @@ class InspectorSmokeTest(manager.InspectorScenarioTest): @test.idempotent_id('a702d1f1-88e4-42ce-88ef-cba2d9e3312e') @test.attr(type='smoke') - @test.services('baremetal', 'object_storage') + @test.services('object_storage') def test_baremetal_introspection(self): """This smoke test case follows this very basic set of operations: diff --git a/ironic_tempest_plugin/tests/test_discovery.py b/ironic_tempest_plugin/tests/test_discovery.py index c4508b7d..439d6d5f 100644 --- a/ironic_tempest_plugin/tests/test_discovery.py +++ b/ironic_tempest_plugin/tests/test_discovery.py @@ -114,7 +114,6 @@ class InspectorDiscoveryTest(manager.InspectorScenarioTest): inspected_node['driver_info'].get(key)) @test.idempotent_id('dd3abe5e-0d23-488d-bb4e-344cdeff7dcb') - @test.services('baremetal', 'compute') def test_bearmetal_auto_discovery(self): """This test case follows this set of operations: From f00d68dd32eb5c9ae77970ff3c36d096a7e330b1 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 5 Jan 2017 12:08:39 +0100 Subject: [PATCH 17/26] Remove unused "service" argument from tempest client manager It was removed by tempest breaking our CI. Change-Id: I8a3311a2d3a9c16f316e1621a270c2a5f9916201 Depends-On: I5cefad7d9d8f6db51fd891ecce9879979c83baee --- ironic_tempest_plugin/services/introspection_client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ironic_tempest_plugin/services/introspection_client.py b/ironic_tempest_plugin/services/introspection_client.py index 5bb03c2e..3b1a75bd 100644 --- a/ironic_tempest_plugin/services/introspection_client.py +++ b/ironic_tempest_plugin/services/introspection_client.py @@ -23,9 +23,8 @@ ADMIN_CREDS = common_creds.get_configured_admin_credentials() class Manager(clients.Manager): def __init__(self, credentials=ADMIN_CREDS, - service=None, api_microversions=None): - super(Manager, self).__init__(credentials, service) + super(Manager, self).__init__(credentials) self.introspection_client = BaremetalIntrospectionClient( self.auth_provider, CONF.baremetal_introspection.catalog_type, From 159fe895f86749fbc505dd6e0ec94491e84209dd Mon Sep 17 00:00:00 2001 From: ghanshyam Date: Fri, 27 Jan 2017 06:35:37 +0000 Subject: [PATCH 18/26] Switch to decorators.idempotent_id test.idempotent_id is being kept temporary to migrate to new lib interface. Now idempotent_id is available as Tempest stable interface decorators and all plugins tests using the old decorator should be switched to new interface. In future, Once all plugins are switched to new decorator Tempest will remove the test.idempotent_id Change-Id: I993fcd22070dca995fb2fb59e876230e8fd0df9e Related-Bug: 1616913 --- ironic_tempest_plugin/tests/test_basic.py | 7 ++++--- ironic_tempest_plugin/tests/test_discovery.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index 7d9cc72d..dd14a40b 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -11,6 +11,7 @@ # under the License. from tempest.config import CONF +from tempest.lib import decorators from tempest import test # noqa from ironic_inspector.test.inspector_tempest_plugin.tests import manager @@ -56,7 +57,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): timeout=CONF.baremetal.active_timeout, interval=self.wait_provisioning_state_interval) - @test.idempotent_id('03bf7990-bee0-4dd7-bf74-b97ad7b52a4b') + @decorators.idempotent_id('03bf7990-bee0-4dd7-bf74-b97ad7b52a4b') @test.services('compute', 'image', 'network', 'object_storage') def test_baremetal_introspection(self): """This smoke test case follows this set of operations: @@ -109,7 +110,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): ins, _node = self.boot_instance() self.terminate_instance(ins) - @test.idempotent_id('70ca3070-184b-4b7d-8892-e977d2bc2870') + @decorators.idempotent_id('70ca3070-184b-4b7d-8892-e977d2bc2870') def test_introspection_abort(self): """This smoke test case follows this very basic set of operations: @@ -146,7 +147,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): class InspectorSmokeTest(manager.InspectorScenarioTest): - @test.idempotent_id('a702d1f1-88e4-42ce-88ef-cba2d9e3312e') + @decorators.idempotent_id('a702d1f1-88e4-42ce-88ef-cba2d9e3312e') @test.attr(type='smoke') @test.services('object_storage') def test_baremetal_introspection(self): diff --git a/ironic_tempest_plugin/tests/test_discovery.py b/ironic_tempest_plugin/tests/test_discovery.py index 439d6d5f..3880f608 100644 --- a/ironic_tempest_plugin/tests/test_discovery.py +++ b/ironic_tempest_plugin/tests/test_discovery.py @@ -14,6 +14,7 @@ import six from ironic_tempest_plugin.tests.scenario import baremetal_manager from tempest import config +from tempest.lib import decorators from tempest import test # noqa from ironic_inspector.test.inspector_tempest_plugin.tests import manager @@ -113,7 +114,7 @@ class InspectorDiscoveryTest(manager.InspectorScenarioTest): self.assertEqual(six.text_type(node_info['driver_info'][key]), inspected_node['driver_info'].get(key)) - @test.idempotent_id('dd3abe5e-0d23-488d-bb4e-344cdeff7dcb') + @decorators.idempotent_id('dd3abe5e-0d23-488d-bb4e-344cdeff7dcb') def test_bearmetal_auto_discovery(self): """This test case follows this set of operations: From 853ec5e7adeca92fdfa8d5ac63ef3e932cd96c4e Mon Sep 17 00:00:00 2001 From: Ken'ichi Ohmichi Date: Thu, 9 Feb 2017 10:04:02 -0800 Subject: [PATCH 19/26] Switch to use test_utils.call_until_true test.call_until_true has been deprecated since Newton on Tempest side, and now Tempest provides test_utils.call_until_true as the stable library method. So this patch switches to use the stable method before removing old test.call_until_true on Tempest side. Change-Id: I5e82466f6eb7b164042406813b1ad72a1cbb05fa --- ironic_tempest_plugin/tests/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index 961b2847..5856716a 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -18,8 +18,8 @@ import time import tempest from tempest import config from tempest.lib.common.api_version_utils import LATEST_MICROVERSION +from tempest.lib.common.utils import test_utils from tempest.lib import exceptions as lib_exc -from tempest import test from ironic_inspector.test.inspector_tempest_plugin import exceptions from ironic_inspector.test.inspector_tempest_plugin.services import \ @@ -152,7 +152,7 @@ class InspectorScenarioTest(BaremetalScenarioTest): return False return True - if not test.call_until_true( + if not test_utils.call_until_true( check_node, duration=CONF.baremetal_introspection.discovery_timeout, sleep_for=20): From f8b09fe503309fa0e35e92941553f47deb02bf0c Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Thu, 16 Feb 2017 10:11:06 -0800 Subject: [PATCH 20/26] Use flake8-import-order Use the flake8 plugin flake8-import-order to check import ordering. It can do it automatically and don't need reviewers to check it. Change-Id: I9ced9c297273db0eec6ab3995b663b1e8dffe87d --- ironic_tempest_plugin/tests/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index 5856716a..343ac9fe 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -12,9 +12,9 @@ import json import os -import six import time +import six import tempest from tempest import config from tempest.lib.common.api_version_utils import LATEST_MICROVERSION From 5d84570ec192c045f1e88c4bd4378b19fb4debdb Mon Sep 17 00:00:00 2001 From: Ngo Quoc Cuong Date: Mon, 22 May 2017 13:23:20 +0700 Subject: [PATCH 21/26] Replace the deprecated tempest.test.attr with decorators.attr [1] moves the attr decorator from test.py to tempest/lib. So, all the references to tempest.test has to be moved to tempest.lib.decorator. [2] https://review.openstack.org/#/c/456236/ Change-Id: I17a6bb8728c9184dd7aeecf7f3b4cab62abd9680 --- ironic_tempest_plugin/tests/test_basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index dd14a40b..bae615eb 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -148,7 +148,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): class InspectorSmokeTest(manager.InspectorScenarioTest): @decorators.idempotent_id('a702d1f1-88e4-42ce-88ef-cba2d9e3312e') - @test.attr(type='smoke') + @decorators.attr(type='smoke') @test.services('object_storage') def test_baremetal_introspection(self): """This smoke test case follows this very basic set of operations: From e76e17e0f4a1a6bbe75058eb76260339396c1b9d Mon Sep 17 00:00:00 2001 From: chenxing Date: Wed, 26 Jul 2017 17:03:16 +0800 Subject: [PATCH 22/26] Update the documentation link for doc migration Change-Id: Ie3d175b4d910f49f8a54812926131448ff1ab4d5 --- ironic_tempest_plugin/README.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 ironic_tempest_plugin/README.rst diff --git a/ironic_tempest_plugin/README.rst b/ironic_tempest_plugin/README.rst new file mode 100644 index 00000000..0ea008c4 --- /dev/null +++ b/ironic_tempest_plugin/README.rst @@ -0,0 +1,18 @@ +======================================= +Tempest Integration of ironic-inspector +======================================= + +This directory contains Tempest tests to cover the ironic-inspector project. + +It uses tempest plugin to automatically load these tests into tempest. More +information about tempest plugin could be found here: +`Plugin `_ + +The legacy method of running Tempest is to just treat the Tempest source code +as a python unittest: +`Run tests `_ + +There is also tox configuration for tempest, use following regex for running +introspection tests:: + + $ tox -e all-plugin -- inspector_tempest_plugin From bdc14290822f44fd6fba208d58c4ec4555b2ba3c Mon Sep 17 00:00:00 2001 From: Luong Anh Tuan Date: Mon, 25 Sep 2017 14:58:10 +0700 Subject: [PATCH 23/26] Replace the usage of 'admin_manager' with 'os_admin' In tempest, alias 'admin_manager' has been moved to 'os_admin'in version Pike, and it will be removed in version Queens[1]. [1]I5f7164f7a7ec5d4380ca22885000caa0183a0bf7 Change-Id: Ic29cb510a558ceee832fbfae7853106decffbb41 Closes-bug: 1697588 --- ironic_tempest_plugin/tests/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index 343ac9fe..1a451ece 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -95,7 +95,7 @@ class InspectorScenarioTest(BaremetalScenarioTest): self.baremetal_client.set_node_provision_state(self, uuid, state) def hypervisor_stats(self): - return (self.admin_manager.hypervisor_client. + return (self.os_admin.hypervisor_client. show_hypervisor_statistics()) def server_show(self, uuid): From 2c22aad1f55c45ce88bed2da00b943e2c6fb11d1 Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Thu, 12 Oct 2017 14:56:24 -0700 Subject: [PATCH 24/26] pep8: Add 'application-import-names = ironic_inspector' Add 'application-import-names = ironic_inspector' to tox.ini. Update files which failed pep8. Change-Id: Ib908b75ac2bf262773978be62e11f973f9eba0d2 --- ironic_tempest_plugin/tests/manager.py | 13 ++++++------- ironic_tempest_plugin/tests/test_basic.py | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ironic_tempest_plugin/tests/manager.py b/ironic_tempest_plugin/tests/manager.py index 1a451ece..e6eb3892 100644 --- a/ironic_tempest_plugin/tests/manager.py +++ b/ironic_tempest_plugin/tests/manager.py @@ -14,6 +14,12 @@ import json import os import time +from ironic_tempest_plugin.tests.api.admin.api_microversion_fixture import \ + APIMicroversionFixture as IronicMicroversionFixture +from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ + BaremetalProvisionStates +from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ + BaremetalScenarioTest import six import tempest from tempest import config @@ -24,13 +30,6 @@ from tempest.lib import exceptions as lib_exc from ironic_inspector.test.inspector_tempest_plugin import exceptions from ironic_inspector.test.inspector_tempest_plugin.services import \ introspection_client -from ironic_tempest_plugin.tests.api.admin.api_microversion_fixture import \ - APIMicroversionFixture as IronicMicroversionFixture -from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ - BaremetalProvisionStates -from ironic_tempest_plugin.tests.scenario.baremetal_manager import \ - BaremetalScenarioTest - CONF = config.CONF diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index bae615eb..536b8536 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -10,12 +10,12 @@ # License for the specific language governing permissions and limitations # under the License. +from ironic_tempest_plugin.tests.scenario import baremetal_manager from tempest.config import CONF from tempest.lib import decorators from tempest import test # noqa from ironic_inspector.test.inspector_tempest_plugin.tests import manager -from ironic_tempest_plugin.tests.scenario import baremetal_manager class InspectorBasicTest(manager.InspectorScenarioTest): From 04aed9fa85142a13f9b4279abb7aa2c1f43f8b55 Mon Sep 17 00:00:00 2001 From: Luong Anh Tuan Date: Mon, 16 Oct 2017 16:21:38 +0700 Subject: [PATCH 25/26] Update tests to do not use deprecated test.services() Function 'tempest.test.services()' has moved to 'tempest.common.utils.services()' in version 'Pike'. This commit update tests accordingly. Change-Id: I30316bf2481ed7f1d963521728eb7cd5f9bd63dc --- ironic_tempest_plugin/tests/test_basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index 536b8536..2dd316c0 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -11,9 +11,9 @@ # under the License. from ironic_tempest_plugin.tests.scenario import baremetal_manager +from tempest.common import utils from tempest.config import CONF from tempest.lib import decorators -from tempest import test # noqa from ironic_inspector.test.inspector_tempest_plugin.tests import manager @@ -58,7 +58,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): interval=self.wait_provisioning_state_interval) @decorators.idempotent_id('03bf7990-bee0-4dd7-bf74-b97ad7b52a4b') - @test.services('compute', 'image', 'network', 'object_storage') + @utils.services('compute', 'image', 'network', 'object_storage') def test_baremetal_introspection(self): """This smoke test case follows this set of operations: @@ -149,7 +149,7 @@ class InspectorSmokeTest(manager.InspectorScenarioTest): @decorators.idempotent_id('a702d1f1-88e4-42ce-88ef-cba2d9e3312e') @decorators.attr(type='smoke') - @test.services('object_storage') + @utils.services('object_storage') def test_baremetal_introspection(self): """This smoke test case follows this very basic set of operations: From 40e2bbc6dd31b044aba6f4aa77a493f19e73fe54 Mon Sep 17 00:00:00 2001 From: ankit Date: Wed, 25 Oct 2017 11:37:02 +0000 Subject: [PATCH 26/26] Add py35 gate for ironic-inspector This patch adds py35 gate for ironic-inspector and also adds support for inspection in python3 environment by rolling out configuration of swift in inspector. Change-Id: I83429a1ba79208245f6c6e1f8b4eb8a16f014868 --- ironic_tempest_plugin/tests/test_basic.py | 7 ++++--- ironic_tempest_plugin/tests/test_discovery.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ironic_tempest_plugin/tests/test_basic.py b/ironic_tempest_plugin/tests/test_basic.py index 2dd316c0..a6087e3d 100644 --- a/ironic_tempest_plugin/tests/test_basic.py +++ b/ironic_tempest_plugin/tests/test_basic.py @@ -21,7 +21,6 @@ from ironic_inspector.test.inspector_tempest_plugin.tests import manager class InspectorBasicTest(manager.InspectorScenarioTest): def verify_node_introspection_data(self, node): - self.assertEqual('yes', node['extra']['rule_success']) data = self.introspection_data(node['uuid']) self.assertEqual(data['cpu_arch'], self.flavor['properties']['cpu_arch']) @@ -58,7 +57,7 @@ class InspectorBasicTest(manager.InspectorScenarioTest): interval=self.wait_provisioning_state_interval) @decorators.idempotent_id('03bf7990-bee0-4dd7-bf74-b97ad7b52a4b') - @utils.services('compute', 'image', 'network', 'object_storage') + @utils.services('compute', 'image', 'network') def test_baremetal_introspection(self): """This smoke test case follows this set of operations: @@ -93,7 +92,9 @@ class InspectorBasicTest(manager.InspectorScenarioTest): for node_id in self.node_ids: node = self.node_show(node_id) - self.verify_node_introspection_data(node) + self.assertEqual('yes', node['extra']['rule_success']) + if CONF.service_available.swift: + self.verify_node_introspection_data(node) self.verify_node_flavor(node) for node_id in self.node_ids: diff --git a/ironic_tempest_plugin/tests/test_discovery.py b/ironic_tempest_plugin/tests/test_discovery.py index 3880f608..f222810d 100644 --- a/ironic_tempest_plugin/tests/test_discovery.py +++ b/ironic_tempest_plugin/tests/test_discovery.py @@ -143,7 +143,8 @@ class InspectorDiscoveryTest(manager.InspectorScenarioTest): inspected_node = self.node_show(self.node_info['name']) self.verify_node_flavor(inspected_node) - self.verify_node_introspection_data(inspected_node) + if CONF.service_available.swift: + self.verify_node_introspection_data(inspected_node) self.verify_node_driver_info(self.node_info, inspected_node) self.assertEqual(ProvisionStates.ENROLL, inspected_node['provision_state'])