# Copyright 2024 Openstack Foundation
# All Rights Reserved.
#
# 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_log import log as logging
from tempest.common import utils
from tempest.common import waiters
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions
from tempest.scenario import manager
CONF = config.CONF
LOG = logging.getLogger(__name__)
[docs]
class TestInstancesWithCinderVolumes(manager.ScenarioTest):
"""This is cinder volumes test.
Tests are below:
* test_instances_with_cinder_volumes_on_all_compute_nodes
"""
compute_min_microversion = '2.60'
[docs]
@decorators.idempotent_id('d0e3c1a3-4b0a-4b0e-8b0a-4b0e8b0a4b0e')
@decorators.attr(type=['slow', 'multinode'])
@utils.services('compute', 'volume', 'image', 'network')
def test_instances_with_cinder_volumes_on_all_compute_nodes(self):
"""Test instances with cinder volumes launches on all compute nodes
Steps:
1. Create an image
2. Create a keypair
3. Create a bootable volume from the image and of the given volume
type
4. Boot an instance from the bootable volume on each available
compute node, up to CONF.compute.min_compute_nodes
5. Create a volume using each volume_types_for_data_volume on all
available compute nodes, up to CONF.compute.min_compute_nodes.
Total number of volumes is equal to
compute nodes * len(volume_types_for_data_volume)
6. Attach volumes to the instances
7. Assign floating IP to all instances
8. Configure security group for ssh access to all instances
9. Confirm ssh access to all instances
10. Run write test to all volumes through ssh connection per
instance
11. Clean up the sources, an instance, volumes, keypair and image
"""
boot_volume_type = (CONF.volume.volume_type or
self.create_volume_type()['name'])
# create an image
image = self.image_create()
# create keypair
keypair = self.create_keypair()
# check all available zones for booting instances
available_zone = \
self.os_admin.availability_zone_client.list_availability_zones(
detail=True)['availabilityZoneInfo']
hosts = []
for zone in available_zone:
if zone['zoneState']['available']:
for host in zone['hosts']:
if 'nova-compute' in zone['hosts'][host] and \
zone['hosts'][host]['nova-compute']['available'] and \
CONF.compute.target_hosts_to_avoid not in host:
hosts.append({'zone': zone['zoneName'],
'host_name': host})
# fail if there is less hosts than minimal number of instances
if len(hosts) < CONF.compute.min_compute_nodes:
raise exceptions.InvalidConfiguration(
"Host list %s is shorter than min_compute_nodes. " % hosts)
# get volume types
volume_types = []
if CONF.volume_feature_enabled.volume_types_for_data_volume:
types = CONF.volume_feature_enabled.volume_types_for_data_volume
volume_types = types.split(',')
else:
# no user specified volume types, create 2 default ones
volume_types.append(self.create_volume_type()['name'])
volume_types.append(self.create_volume_type()['name'])
hosts_to_boot_servers = hosts[:CONF.compute.min_compute_nodes]
LOG.debug("List of hosts selected to boot servers %s: ",
hosts_to_boot_servers)
# create volumes so that we dont need to wait for them to be created
# and save them in a list
created_volumes = []
for host in hosts_to_boot_servers:
for volume_type in volume_types:
created_volumes.append(
self.create_volume(volume_type=volume_type,
wait_until=None)
)
bootable_volumes = []
for host in hosts_to_boot_servers:
# create boot volume from image and of the given volume type
bootable_volumes.append(
self.create_volume(
imageRef=image, volume_type=boot_volume_type,
wait_until=None)
)
# boot server
servers = []
for bootable_volume in bootable_volumes:
# wait for bootable volumes to become available
waiters.wait_for_volume_resource_status(
self.volumes_client, bootable_volume['id'], 'available')
# create an instance from bootable volume
server = self.boot_instance_from_resource(
source_id=bootable_volume['id'],
source_type='volume',
keypair=keypair,
wait_until=None
)
servers.append(server)
start = 0
end = len(volume_types)
for server in servers:
attached_volumes = []
# wait for server to become active
waiters.wait_for_server_status(self.servers_client,
server['id'], 'ACTIVE')
# attach volumes to the instances
for volume in created_volumes[start:end]:
# wait for volume to become available
waiters.wait_for_volume_resource_status(
self.volumes_client, volume['id'], 'available')
attached_volume = self.nova_volume_attach(server, volume)
attached_volumes.append(attached_volume)
LOG.debug("Attached volume %s to server %s",
attached_volume['id'], server['id'])
# assign floating ip
floating_ip = None
if (CONF.network_feature_enabled.floating_ips and
CONF.network.floating_network_name):
fip = self.create_floating_ip(server)
floating_ip = self.associate_floating_ip(
fip, server)
ssh_ip = floating_ip['floating_ip_address']
else:
ssh_ip = self.get_server_ip(server)
# create security group
self.create_and_add_security_group_to_server(server)
# confirm ssh access
self.linux_client = self.get_remote_client(
ssh_ip, private_key=keypair['private_key'],
server=server
)
server_name = server['name'].split('-')[-1]
# run write test on all volumes
for volume in attached_volumes:
# dev name volume['attachments'][0]['device'][5:] is like
# /dev/vdb, we need to remove /dev/ -> first 5 chars
dev_name = volume['attachments'][0]['device'][5:]
mount_path = f"/mnt/{server_name}"
timestamp_before = self.create_timestamp(
ssh_ip, private_key=keypair['private_key'], server=server,
dev_name=dev_name, mount_path=mount_path,
)
timestamp_after = self.get_timestamp(
ssh_ip, private_key=keypair['private_key'], server=server,
dev_name=dev_name, mount_path=mount_path,
)
self.assertEqual(timestamp_before, timestamp_after)
# delete volume
self.nova_volume_detach(server, volume)
self.volumes_client.delete_volume(volume['id'])
if floating_ip:
# delete the floating IP, this should refresh the server
# addresses
self.disassociate_floating_ip(floating_ip)
waiters.wait_for_server_floating_ip(
self.servers_client, server, floating_ip,
wait_for_disassociate=True)
start += len(volume_types)
end += len(volume_types)