diff --git a/devstack/lib/ironic b/devstack/lib/ironic index 2dc1e9992d..e4b812c305 100644 --- a/devstack/lib/ironic +++ b/devstack/lib/ironic @@ -1916,6 +1916,11 @@ function prepare_baremetal_basic_ops { if [[ "$IRONIC_IS_HARDWARE" == "False" ]]; then configure_ironic_auxiliary fi + + if ! is_service_enabled nova && [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then + sudo install -g $LIBVIRT_GROUP -o $STACK_USER -m 644 $FILES/${IRONIC_WHOLEDISK_IMAGE_NAME}.img $IRONIC_HTTP_DIR + fi + upload_baremetal_ironic_deploy enroll_nodes configure_tftpd @@ -2004,6 +2009,12 @@ function ironic_configure_tempest { iniset $TEMPEST_CONFIG baremetal whole_disk_image_ref $image_uuid image_uuid=$(openstack image show $IRONIC_PARTITIONED_IMAGE_NAME -f value -c id) iniset $TEMPEST_CONFIG baremetal partition_image_ref $image_uuid + + if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then + iniset $TEMPEST_CONFIG baremetal whole_disk_image_url "http://$IRONIC_HTTP_SERVER:$IRONIC_HTTP_PORT/${IRONIC_WHOLEDISK_IMAGE_NAME}.img" + iniset $TEMPEST_CONFIG baremetal whole_disk_image_checksum $(md5sum $FILES/${IRONIC_WHOLEDISK_IMAGE_NAME}.img) + fi + iniset $TEMPEST_CONFIG baremetal enabled_drivers $IRONIC_ENABLED_DRIVERS iniset $TEMPEST_CONFIG baremetal enabled_hardware_types $IRONIC_ENABLED_HARDWARE_TYPES @@ -2021,6 +2032,9 @@ function ironic_configure_tempest { iniset $TEMPEST_CONFIG baremetal deploywait_timeout $IRONIC_TEMPEST_BUILD_TIMEOUT iniset $TEMPEST_CONFIG baremetal power_timeout $IRONIC_TEMPEST_BUILD_TIMEOUT fi + + # Enabled features + iniset $TEMPEST_CONFIG baremetal_feature_enabled ipxe_enabled $IRONIC_IPXE_ENABLED } function get_ironic_node_prefix { diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py index a7afcd6d72..661b89c2ca 100644 --- a/ironic_tempest_plugin/config.py +++ b/ironic_tempest_plugin/config.py @@ -34,6 +34,10 @@ baremetal_group = cfg.OptGroup(name='baremetal', 'live_migration, pause, rescue, resize, ' 'shelve, snapshot, and suspend') +baremetal_features_group = cfg.OptGroup( + name='baremetal_feature_enabled', + title="Enabled Baremetal Service Features") + BaremetalGroup = [ cfg.StrOpt('catalog_type', default='baremetal', @@ -86,6 +90,11 @@ BaremetalGroup = [ help="Whether the Ironic/Neutron tenant isolation is enabled"), cfg.StrOpt('whole_disk_image_ref', help="UUID of the wholedisk image to use in the tests."), + cfg.StrOpt('whole_disk_image_url', + help="An http link to the wholedisk image to use in the " + "tests."), + cfg.StrOpt('whole_disk_image_checksum', + help="An MD5 checksum of the image."), cfg.StrOpt('partition_image_ref', help="UUID of the partitioned image to use in the tests."), cfg.ListOpt('enabled_drivers', @@ -99,3 +108,9 @@ BaremetalGroup = [ help="Ironic adjusted disk size to use in the standalone tests " "as instance_info/root_gb value."), ] + +BaremetalFeaturesGroup = [ + cfg.BoolOpt('ipxe_enabled', + default=True, + help="Defines if IPXE is enabled"), +] diff --git a/ironic_tempest_plugin/plugin.py b/ironic_tempest_plugin/plugin.py index dbe0f0b136..9e9c175c6a 100644 --- a/ironic_tempest_plugin/plugin.py +++ b/ironic_tempest_plugin/plugin.py @@ -21,6 +21,12 @@ from tempest.test_discover import plugins from ironic_tempest_plugin import config as project_config +_opts = [ + (project_config.baremetal_group, project_config.BaremetalGroup), + (project_config.baremetal_features_group, + project_config.BaremetalFeaturesGroup) +] + class IronicTempestPlugin(plugins.TempestPlugin): def load_tests(self): @@ -33,9 +39,8 @@ class IronicTempestPlugin(plugins.TempestPlugin): def register_opts(self, conf): conf.register_opt(project_config.service_option, group='service_available') - config.register_opt_group(conf, project_config.baremetal_group, - project_config.BaremetalGroup) + for group, option in _opts: + config.register_opt_group(conf, group, option) def get_opt_lists(self): - return [(project_config.baremetal_group.name, - project_config.BaremetalGroup)] + return [(group.name, option) for group, option in _opts] diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py index 8415a48001..507513b53c 100644 --- a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py +++ b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py @@ -206,7 +206,7 @@ class BaremetalStandaloneManager(bm.BaremetalScenarioTest, return nodes[0] @classmethod - def boot_node(cls, driver, image_ref): + def boot_node(cls, driver, image_ref, image_checksum=None): """Boot ironic node. The following actions are executed: @@ -220,6 +220,8 @@ class BaremetalStandaloneManager(bm.BaremetalScenarioTest, :param driver: Node driver to use. :param image_ref: Reference to user image to boot node with. + :param image_checksum: md5sum of image specified in image_ref. + Needed only when direct HTTP link is provided. :returns: Ironic node. """ node = cls.get_and_reserve_node() @@ -230,6 +232,10 @@ class BaremetalStandaloneManager(bm.BaremetalScenarioTest, patch = [{'path': '/instance_info/image_source', 'op': 'add', 'value': image_ref}] + if image_checksum is not None: + patch.append({'path': '/instance_info/image_checksum', + 'op': 'add', + 'value': image_checksum}) patch.append({'path': '/instance_info/root_gb', 'op': 'add', 'value': CONF.baremetal.adjusted_root_disk_size_gb}) @@ -280,6 +286,9 @@ class BaremetalStandaloneScenarioTest(BaremetalStandaloneManager): # Boolean value specify if image is wholedisk or not. wholedisk_image = None + # Image checksum, required when image is stored on HTTP server. + image_checksum = None + mandatory_attr = ['driver', 'image_ref', 'wholedisk_image'] node = None @@ -310,7 +319,11 @@ class BaremetalStandaloneScenarioTest(BaremetalStandaloneManager): if getattr(cls, v) is None: raise lib_exc.InvalidConfiguration( "Mandatory attribute %s not set." % v) - cls.node = cls.boot_node(cls.driver, cls.image_ref) + image_checksum = None + if not uuidutils.is_uuid_like(cls.image_ref): + image_checksum = cls.image_checksum + cls.node = cls.boot_node(cls.driver, cls.image_ref, + image_checksum=image_checksum) cls.node_ip = cls.add_floatingip_to_node(cls.node['uuid']) @classmethod diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py index 4def3fa4b6..72b3236fd4 100644 --- a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py +++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py @@ -34,6 +34,27 @@ class BaremetalAgentIpmitoolWholedisk(bsm.BaremetalStandaloneScenarioTest): self.ping_ip_address(self.node_ip, should_succeed=True) +class BaremetalAgentIpmitoolWholediskHttpLink( + bsm.BaremetalStandaloneScenarioTest): + + driver = 'agent_ipmitool' + image_ref = CONF.baremetal.whole_disk_image_url + image_checksum = CONF.baremetal.whole_disk_image_checksum + wholedisk_image = True + + @classmethod + def skip_checks(cls): + super(BaremetalAgentIpmitoolWholediskHttpLink, cls).skip_checks() + if not CONF.baremetal_feature_enabled.ipxe_enabled: + skip_msg = ("HTTP server is not available when ipxe is disabled.") + raise cls.skipException(skip_msg) + + @test.idempotent_id('d926c683-1a32-44df-afd0-e60134346fd0') + @test.services('network') + def test_ip_access_to_server(self): + self.ping_ip_address(self.node_ip, should_succeed=True) + + class BaremetalAgentIpmitoolPartitioned(bsm.BaremetalStandaloneScenarioTest): driver = 'agent_ipmitool' @@ -58,6 +79,27 @@ class BaremetalPxeIpmitoolWholedisk(bsm.BaremetalStandaloneScenarioTest): self.ping_ip_address(self.node_ip, should_succeed=True) +class BaremetalPxeIpmitoolWholediskHttpLink( + bsm.BaremetalStandaloneScenarioTest): + + driver = 'pxe_ipmitool' + image_ref = CONF.baremetal.whole_disk_image_url + image_checksum = CONF.baremetal.whole_disk_image_checksum + wholedisk_image = True + + @classmethod + def skip_checks(cls): + super(BaremetalPxeIpmitoolWholediskHttpLink, cls).skip_checks() + if not CONF.baremetal_feature_enabled.ipxe_enabled: + skip_msg = ("HTTP server is not available when ipxe is disabled.") + raise cls.skipException(skip_msg) + + @test.idempotent_id('71ccf06f-6765-40fd-8252-1b1bfa423b9b') + @test.services('network') + def test_ip_access_to_server(self): + self.ping_ip_address(self.node_ip, should_succeed=True) + + class BaremetalPxeIpmitoolPartitioned(bsm.BaremetalStandaloneScenarioTest): driver = 'pxe_ipmitool'