ironic-tempest-plugin/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py

492 lines
18 KiB
Python

#
# Copyright 2017 Mirantis Inc.
#
# Copyright (c) 2022 Dell Inc. or its subsidiaries.
#
# 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
import os
import jsonschema
from jsonschema import exceptions as json_schema_exc
from oslo_log import log as logging
from tempest.common import utils
from tempest import config
from tempest.lib import decorators
from ironic_tempest_plugin import exceptions
from ironic_tempest_plugin.tests.scenario import \
baremetal_standalone_manager as bsm
LOG = logging.getLogger(__name__)
CONF = config.CONF
class BaremetalCleaningAgentIpmitoolWholedisk(
bsm.BaremetalStandaloneScenarioTest):
driver = 'agent_ipmitool'
image_ref = CONF.baremetal.whole_disk_image_ref
wholedisk_image = True
delete_node = False
api_microversion = '1.28'
@decorators.idempotent_id('0d82cedd-9697-4cf7-8e4a-80d510f53615')
@utils.services('image', 'network')
def test_manual_cleaning(self):
self.check_manual_partition_cleaning(self.node)
class BaremetalCleaningPxeIpmitoolWholedisk(
bsm.BaremetalStandaloneScenarioTest):
driver = 'pxe_ipmitool'
image_ref = CONF.baremetal.whole_disk_image_ref
wholedisk_image = True
delete_node = False
api_microversion = '1.28'
@decorators.idempotent_id('fb03abfa-cdfc-41ec-aaa8-c70402786a85')
@utils.services('image', 'network')
def test_manual_cleaning(self):
self.check_manual_partition_cleaning(self.node)
class BaremetalCleaningIpmiWholedisk(
bsm.BaremetalStandaloneScenarioTest):
driver = 'ipmi'
image_ref = CONF.baremetal.whole_disk_image_ref
wholedisk_image = True
delete_node = False
deploy_interface = 'iscsi'
api_microversion = '1.31'
@classmethod
def skip_checks(cls):
super(BaremetalCleaningIpmiWholedisk, cls).skip_checks()
if CONF.baremetal_feature_enabled.software_raid:
raise cls.skipException("Cleaning is covered in the RAID test")
@decorators.idempotent_id('065238db-1b6d-4d75-a9da-c240f8cbd956')
@utils.services('image', 'network')
def test_manual_cleaning(self):
self.check_manual_partition_cleaning(self.node)
class SoftwareRaidIscsi(bsm.BaremetalStandaloneScenarioTest):
if 'redfish' in CONF.baremetal.enabled_hardware_types:
driver = 'redfish'
else:
driver = 'ipmi'
image_ref = CONF.baremetal.whole_disk_image_ref
wholedisk_image = True
deploy_interface = 'iscsi'
raid_interface = 'agent'
api_microversion = '1.55'
# Software RAID is always local boot
boot_option = 'local'
delete_node = False
raid_config = {
"logical_disks": [
{
"size_gb": "MAX",
"raid_level": "1",
"controller": "software"
},
]
}
@classmethod
def skip_checks(cls):
super(SoftwareRaidIscsi, cls).skip_checks()
if cls.driver == 'ipmi':
raise cls.skipException("Testing with redfish driver")
if not CONF.baremetal_feature_enabled.software_raid:
raise cls.skipException("Software RAID feature is not enabled")
@decorators.idempotent_id('7ecba4f7-98b8-4ea1-b95e-3ec399f46798')
@utils.services('image', 'network')
def test_software_raid(self):
self.build_raid_and_verify_node(
deploy_time=CONF.baremetal_feature_enabled.deploy_time_raid)
# NOTE(TheJulia): tearing down/terminating the instance does not
# remove the root device hint, so it is best for us to go ahead
# and remove it before exiting the test.
self.remove_root_device_hint()
self.terminate_node(self.node['uuid'], force_delete=True)
class SoftwareRaidDirect(bsm.BaremetalStandaloneScenarioTest):
if 'redfish' in CONF.baremetal.enabled_hardware_types:
driver = 'redfish'
else:
driver = 'ipmi'
image_ref = CONF.baremetal.whole_disk_image_ref
wholedisk_image = True
deploy_interface = 'direct'
raid_interface = 'agent'
api_microversion = '1.55'
# Software RAID is always local boot
boot_option = 'local'
delete_node = False
# TODO(dtantsur): more complex layout in this job
raid_config = {
"logical_disks": [
{
"size_gb": "MAX",
"raid_level": "1",
"controller": "software"
},
]
}
@classmethod
def skip_checks(cls):
super(SoftwareRaidDirect, cls).skip_checks()
if cls.driver == 'redfish':
raise cls.skipException("Testing with ipmi driver")
if not CONF.baremetal_feature_enabled.software_raid:
raise cls.skipException("Software RAID feature is not enabled")
@decorators.idempotent_id('125361ac-0eb3-4d79-8be2-a91936aa3f46')
@utils.services('image', 'network')
def test_software_raid(self):
self.build_raid_and_verify_node(
deploy_time=CONF.baremetal_feature_enabled.deploy_time_raid)
# NOTE(TheJulia): tearing down/terminating the instance does not
# remove the root device hint, so it is best for us to go ahead
# and remove it before exiting the test.
self.remove_root_device_hint()
self.terminate_node(self.node['uuid'], force_delete=True)
class BaremetalIdracManagementCleaning(
bsm.BaremetalStandaloneScenarioTest):
mandatory_attr = ['driver', 'management_interface',
'power_interface']
driver = 'idrac'
delete_node = False
# Minimum version for manual cleaning is 1.15 (# v1.15: Add ability to
# do manual cleaning of nodes). The test cases clean up at the end by
# detaching the VIF. Support for VIFs was introduced by version 1.28
# (# v1.28: Add vifs subcontroller to node).
api_microversion = '1.28'
@decorators.idempotent_id('d085ff72-abef-4931-a5b0-06efd5f9a037')
def test_reset_idrac(self):
clean_steps = [
{
"interface": "management",
"step": "reset_idrac"
}
]
self.manual_cleaning(self.node, clean_steps=clean_steps)
@decorators.idempotent_id('9252ec6f-6b5b-447e-a323-c52775b88b4e')
def test_clear_job_queue(self):
clean_steps = [
{
"interface": "management",
"step": "clear_job_queue"
}
]
self.manual_cleaning(self.node, clean_steps=clean_steps)
@decorators.idempotent_id('7baeff52-7d6e-4dea-a48f-a85a6bfc9f62')
def test_known_good_state(self):
clean_steps = [
{
"interface": "management",
"step": "known_good_state"
}
]
self.manual_cleaning(self.node, clean_steps=clean_steps)
class BaremetalIdracRedfishManagementCleaning(
BaremetalIdracManagementCleaning):
management_interface = 'idrac-redfish'
power_interface = 'idrac-redfish'
class BaremetalIdracWSManManagementCleaning(
BaremetalIdracManagementCleaning):
management_interface = 'idrac-wsman'
power_interface = 'idrac-wsman'
class BaremetalIdracRaidCleaning(bsm.BaremetalStandaloneScenarioTest):
mandatory_attr = ['driver', 'raid_interface']
image_ref = CONF.baremetal.whole_disk_image_ref
wholedisk_image = True
storage_inventory_info = None
driver = 'idrac'
api_microversion = '1.31' # to set raid_interface
delete_node = False
@classmethod
def skip_checks(cls):
"""Validates the storage information passed in file using JSON schema.
:raises: skipException if,
1) storage inventory path is not provided in tempest execution
file.
2) storage inventory file is not found on given path.
:raises: RaidCleaningInventoryValidationFailed if,
validation of the storage inventory fails.
"""
super(BaremetalIdracRaidCleaning, cls).skip_checks()
storage_inventory = CONF.baremetal.storage_inventory_file
if not storage_inventory:
raise cls.skipException("Storage inventory file path missing "
"in tempest configuration file. "
"Skipping Test case.")
try:
with open(storage_inventory, 'r') as storage_invent_fobj:
cls.storage_inventory_info = json.load(storage_invent_fobj)
except IOError:
msg = ("Storage Inventory file %(inventory)s is not found. "
"Skipping Test Case." %
{'inventory': storage_inventory})
raise cls.skipException(msg)
storage_inventory_schema = os.path.join(os.path.dirname(
__file__), 'storage_inventory_schema.json')
with open(storage_inventory_schema, 'r') as storage_schema_fobj:
schema = json.load(storage_schema_fobj)
try:
jsonschema.validate(cls.storage_inventory_info, schema)
except json_schema_exc.ValidationError as e:
error_msg = ("Storage Inventory validation error: %(error)s " %
{'error': e})
raise exceptions.RaidCleaningInventoryValidationFailed(error_msg)
def _validate_raid_type_and_drives_count(self, raid_type,
minimum_drives_required):
for controller in (self.storage_inventory_info[
'storage_inventory']['controllers']):
supported_raid_types = controller['supported_raid_types']
physical_disks = [pdisk['id'] for pdisk in (
controller['drives'])]
if raid_type in supported_raid_types and (
minimum_drives_required <= len(physical_disks)):
return controller
error_msg = ("No Controller present in storage inventory which "
"supports RAID type %(raid_type)s "
"and has at least %(disk_count)s drives." %
{'raid_type': raid_type,
'disk_count': minimum_drives_required})
raise exceptions.RaidCleaningInventoryValidationFailed(error_msg)
@decorators.idempotent_id('8a908a3c-f2af-48fb-8553-9163715aa403')
@utils.services('image', 'network')
def test_hardware_raid(self):
controller = self._validate_raid_type_and_drives_count(
raid_type='1', minimum_drives_required=2)
raid_config = {
"logical_disks": [
{
"size_gb": 40,
"raid_level": "1",
"controller": controller['id']
}
]
}
self.build_raid_and_verify_node(
config=raid_config,
deploy_time=CONF.baremetal_feature_enabled.deploy_time_raid,
erase_device_metadata=False)
self.remove_root_device_hint()
self.terminate_node(self.node['uuid'], force_delete=True)
@decorators.idempotent_id('92fe534d-77f1-422d-84e4-e30fe9e3d928')
@utils.services('image', 'network')
def test_raid_cleaning_max_size_raid_10(self):
controller = self._validate_raid_type_and_drives_count(
raid_type='1+0', minimum_drives_required=4)
physical_disks = [pdisk['id'] for pdisk in (
controller['drives'])]
raid_config = {
"logical_disks": [
{
"size_gb": "MAX",
"raid_level": "1+0",
"controller": controller['id'],
"physical_disks": physical_disks
}
]
}
self.build_raid_and_verify_node(
config=raid_config,
deploy_time=CONF.baremetal_feature_enabled.deploy_time_raid,
erase_device_metadata=False)
self.remove_root_device_hint()
self.terminate_node(self.node['uuid'], force_delete=True)
class BaremetalIdracRedfishRaidCleaning(
BaremetalIdracRaidCleaning):
raid_interface = 'idrac-redfish'
class BaremetalIdracWSManRaidCleaning(
BaremetalIdracRaidCleaning):
raid_interface = 'idrac-wsman'
class BaremetalRedfishFirmwareUpdate(bsm.BaremetalStandaloneScenarioTest):
api_microversion = '1.68' # to support redfish firmware update
driver = 'redfish'
delete_node = False
image_ref = CONF.baremetal.whole_disk_image_ref
wholedisk_image = True
@classmethod
def skip_checks(cls):
super(BaremetalRedfishFirmwareUpdate, cls).skip_checks()
if not CONF.baremetal.firmware_image_url:
raise cls.skipException("Firmware image URL is not "
"provided. Skipping test case.")
if not CONF.baremetal.firmware_image_checksum:
raise cls.skipException("Firmware image SHA1 checksum is not "
"provided. Skipping test case.")
def _firmware_update(self, fw_image_url, fw_image_checksum):
steps = [
{
"interface": "management",
"step": "update_firmware",
"args": {
"firmware_images": [
{
"url": fw_image_url,
"checksum": fw_image_checksum,
"wait": 300
}
]
}
}
]
self.manual_cleaning(self.node, clean_steps=steps)
@utils.services('network')
@decorators.idempotent_id('360e0c0e-3c17-4d2e-b052-55a932c1a4c7')
def test_firmware_update(self):
# WARNING: Removing power from a server while it is in the process of
# updating firmware may result in devices in the server, or the server
# itself becoming inoperable.
# Execution of firmware test case needs careful execution as it
# changes state of server, may result in break down of server on
# interruption. As it deals with firmware of component, make sure
# to provide proper image url path while testing with correct SHA1
# checksum of image.
self._firmware_update(CONF.baremetal.firmware_image_url,
CONF.baremetal.firmware_image_checksum)
if (CONF.baremetal.firmware_rollback_image_url
and CONF.baremetal.firmware_rollback_image_checksum):
self.addCleanup(self._firmware_update,
CONF.baremetal.firmware_rollback_image_url,
CONF.baremetal.firmware_rollback_image_checksum)
class BaremetalIdracRedfishFirmwareUpdate(BaremetalRedfishFirmwareUpdate):
driver = 'idrac'
boot_interface = 'ipxe'
management_interface = 'idrac-redfish'
power_interface = 'idrac-redfish'
class BaremetalIdracRedfishConfiguartionMolds(
bsm.BaremetalStandaloneScenarioTest):
api_microversion = '1.72' # to support configuration molds functionality
delete_node = False
image_ref = CONF.baremetal.whole_disk_image_ref
driver = 'idrac'
boot_interface = 'ipxe'
management_interface = 'idrac-redfish'
power_interface = 'idrac-redfish'
wholedisk_image = True
@utils.services('network')
@decorators.idempotent_id('69386cf6-bbf2-451e-b927-f68c66075f02')
def test_configuration_molds_export(self):
if not CONF.baremetal.export_location:
raise self.skipException("Export configuration location path "
"is not provided. Skipping test case. "
"Make sure to provide correct "
"configuration path in which "
"configuration would be exported. "
"In case of Swift object storage, "
"make to sure to provide proper "
"container path.")
steps = [
{
"interface": "management",
"step": "export_configuration",
"args": {
"export_configuration_location":
CONF.baremetal.export_location
}
}
]
self.manual_cleaning(self.node, clean_steps=steps)
@utils.services('network')
@decorators.idempotent_id('cad15719-c293-4338-8fa9-986ef02b4682')
def test_configuration_molds_import(self):
if not CONF.baremetal.import_location:
raise self.skipException("Import configuration JSON file location "
"is not provided. Make sure to provide "
"correct configuration file with proper "
"configuration which needs to be tested "
"during execution of this test. "
"Skipping test case.")
steps = [
{
"interface": "management",
"step": "import_configuration",
"args": {
"import_configuration_location":
CONF.baremetal.import_location
}
}
]
self.manual_cleaning(self.node, clean_steps=steps)
if CONF.baremetal.rollback_import_location:
rollback_steps = [
{
"interface": "management",
"step": "import_configuration",
"args": {
"import_configuration_location":
CONF.baremetal.rollback_import_location
}
}
]
self.addCleanup(self.manual_cleaning, self.node,
clean_steps=rollback_steps)