add manila+VM test

create NFS share and access it from VM

Change-Id: I588251b032ce3f283626b4ca8032b517920d7e83
This commit is contained in:
Marian Gasparovic 2020-04-02 12:03:11 +02:00
parent 771c922af5
commit 93ee1c074f
6 changed files with 361 additions and 3 deletions

View File

@ -14,16 +14,22 @@
# under the License.
from rally.common import logging
from rally import exceptions
from rally.task import types
from rally.task import utils as rally_utils
from rally.task import validation
from rally_openstack.common import consts
from rally_openstack.task.contexts.manila import consts as manila_consts
from rally_openstack.task import scenario
from rally_openstack.task.scenarios.manila import utils
from rally_openstack.task.scenarios.vm import utils as vm_utils
"""Scenarios for Manila shares."""
LOG = logging.getLogger(__name__)
@validation.add("enum", param_name="share_proto",
values=["NFS", "CIFS", "GLUSTERFS", "HDFS", "CEPHFS"],
@ -56,6 +62,113 @@ class CreateAndDeleteShare(utils.ManilaScenario):
self.sleep_between(min_sleep, max_sleep)
self._delete_share(share)
@types.convert(image={"type": "glance_image"},
flavor={"type": "nova_flavor"})
@validation.add("image_valid_on_flavor", flavor_param="flavor",
image_param="image", fail_on_404_image=False)
@validation.add("number", param_name="port", minval=1, maxval=65535,
nullable=True, integer_only=True)
@validation.add("external_network_exists", param_name="floating_network")
@validation.add("required_services", services=[consts.Service.MANILA,
consts.Service.NOVA])
@validation.add("required_platform", platform="openstack", users=True)
@scenario.configure(context={"cleanup@openstack": ["manila", "nova"],
"keypair@openstack": {},
"allow_ssh@openstack": None},
name="ManilaShares.create_share_and_access_from_vm",
platform="openstack")
class CreateShareAndAccessFromVM(utils.ManilaScenario, vm_utils.VMScenario):
def run(self, image, flavor, username, size=1, password=None,
floating_network=None, port=22,
use_floating_ip=True, force_delete=False, max_log_length=None,
**kwargs):
"""Create a share and access it from a VM.
- create NFS share
- launch VM
- authorize VM's fip to access the share
- mount share iside the VM
- write to share
- delete VM
- delete share
:param size: share size in GB, should be greater than 0
:param image: glance image name to use for the vm
:param flavor: VM flavor name
:param username: ssh username on server
:param password: Password on SSH authentication
:param floating_network: external network name, for floating ip
:param port: ssh port for SSH connection
:param use_floating_ip: bool, floating or fixed IP for SSH connection
:param force_delete: whether to use force_delete for servers
:param max_log_length: The number of tail nova console-log lines user
would like to retrieve
:param kwargs: optional args to create a share or a VM
"""
share_proto = "nfs"
share = self._create_share(
share_proto=share_proto,
size=size,
**kwargs)
location = self._export_location(share)
server, fip = self._boot_server_with_fip(
image, flavor, use_floating_ip=use_floating_ip,
floating_network=floating_network,
key_name=self.context["user"]["keypair"]["name"],
userdata="#cloud-config\npackages:\n - nfs-common",
**kwargs)
self._allow_access_share(share, "ip", fip["ip"], "rw")
mount_opt = "-t nfs -o nfsvers=4.1,proto=tcp"
script = f"cloud-init status -w;" \
f"sudo mount {mount_opt} {location[0]} /mnt || exit 1;" \
f"sudo touch /mnt/testfile || exit 2"
command = {
"script_inline": script,
"interpreter": "/bin/bash"
}
try:
rally_utils.wait_for_status(
server,
ready_statuses=["ACTIVE"],
update_resource=rally_utils.get_from_manager(),
)
code, out, err = self._run_command(
fip["ip"], port, username, password, command=command)
if code:
raise exceptions.ScriptError(
"Error running command %(command)s. "
"Error %(code)s: %(error)s" % {
"command": command, "code": code, "error": err})
except (exceptions.TimeoutException,
exceptions.SSHTimeout):
console_logs = self._get_server_console_output(server,
max_log_length)
LOG.debug("VM console logs:\n%s" % console_logs)
raise
finally:
self._delete_server_with_fip(server, fip,
force_delete=force_delete)
self._delete_share(share)
self.add_output(complete={
"title": "Script StdOut",
"chart_plugin": "TextArea",
"data": str(out).split("\n")
})
if err:
self.add_output(complete={
"title": "Script StdErr",
"chart_plugin": "TextArea",
"data": err.split("\n")
})
@validation.add("required_services", services=[consts.Service.MANILA])
@validation.add("required_platform", platform="openstack", users=True)
@ -242,9 +355,9 @@ class ListShareServers(utils.ManilaScenario):
self._list_share_servers(search_opts=search_opts)
@validation.add("enum", param_name="share_proto", values=["nfs", "cephfs",
"cifs", "glusterfs", "hdfs"], missed=False,
case_insensitive=True)
@validation.add("enum", param_name="share_proto",
values=["nfs", "cephfs", "cifs", "glusterfs", "hdfs"],
missed=False, case_insensitive=True)
@validation.add("required_services", services=[consts.Service.MANILA])
@validation.add("required_platform", platform="openstack", users=True)
@scenario.configure(

View File

@ -86,6 +86,14 @@ class ManilaScenario(scenario.OpenStackScenario):
timeout=CONF.openstack.manila_share_delete_timeout,
check_interval=CONF.openstack.manila_share_delete_poll_interval)
def _export_location(self, share):
"""Export share location.
:param share: :class:`Share`
"""
location = share.export_locations
return location
def _get_access_from_share(self, share, access_id):
"""Get access from share

View File

@ -0,0 +1,44 @@
{
"ManilaShares.create_share_and_access_from_vm": [
{
"args": {
"size": 1,
"share_type": "CephNFStype",
"flavor": {
"name": "m1.tiny"
},
"image": {
"name": "^cirros.*-disk$"
},
"username": "ubuntu"
},
"runner": {
"type": "constant",
"times": 2,
"concurrency": 2
},
"context": {
"quotas": {
"manila": {
"shares": -1,
"gigabytes": -1
}
},
"users": {
"tenants": 2,
"users_per_tenant": 1
},
"network": {
"dns_nameservers": [
"9.9.9.9"
]
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,28 @@
ManilaShares.create_share_and_access_from_vm:
-
args:
size: 1
share_type: "CephNFStype"
flavor:
name: "m1.tiny"
image:
name: "^cirros.*-disk$"
username: "ubuntu"
runner:
type: "constant"
times: 2
concurrency: 2
context:
quotas:
manila:
shares: -1
gigabytes: -1
users:
tenants: 2
users_per_tenant: 1
network:
dns_nameservers:
- "9.9.9.9"
sla:
failure_rate:
max: 0

View File

@ -17,6 +17,7 @@ from unittest import mock
import ddt
from rally import exceptions
from rally_openstack.task.scenarios.manila import shares
from tests.unit import test
@ -42,6 +43,164 @@ class ManilaSharesTestCase(test.ScenarioTestCase):
scenario.sleep_between.assert_called_once_with(3, 4)
scenario._delete_share.assert_called_once_with(fake_share)
def create_env(self, scenario):
fake_share = mock.MagicMock()
scenario = shares.CreateShareAndAccessFromVM(self.context)
self.ip = {"id": "foo_id", "ip": "foo_ip", "is_floating": True}
scenario._boot_server_with_fip = mock.Mock(
return_value=("foo_server", self.ip))
scenario._delete_server_with_fip = mock.Mock()
scenario._run_command = mock.MagicMock(
return_value=(0, "{\"foo\": 42}", "foo_err"))
scenario.add_output = mock.Mock()
self.context.update({"user": {"keypair": {"name": "keypair_name"},
"credential": mock.MagicMock()}})
scenario._create_share = mock.MagicMock(return_value=fake_share)
scenario._delete_share = mock.MagicMock()
scenario._export_location = mock.MagicMock(return_value="fake")
scenario._allow_access_share = mock.MagicMock()
return scenario, fake_share
@ddt.data(
{"image": "some_image",
"flavor": "m1.small", "username": "chuck norris"}
)
@mock.patch("rally.task.utils.get_from_manager")
@mock.patch("rally.task.utils.wait_for_status")
def test_create_share_and_access_from_vm(
self,
params,
mock_rally_task_utils_wait_for_status,
mock_rally_task_utils_get_from_manager):
scenario, fake_share = self.create_env(
shares.CreateShareAndAccessFromVM(self.context))
scenario.run(**params)
scenario._create_share.assert_called_once_with(
share_proto="nfs", size=1)
scenario._delete_share.assert_called_once_with(fake_share)
scenario._allow_access_share.assert_called_once_with(
fake_share, "ip", "foo_ip", "rw")
scenario._export_location.assert_called_once_with(fake_share)
scenario._boot_server_with_fip.assert_called_once_with(
"some_image", "m1.small", use_floating_ip=True,
floating_network=None, key_name="keypair_name",
userdata="#cloud-config\npackages:\n - nfs-common")
mock_rally_task_utils_wait_for_status.assert_called_once_with(
"foo_server", ready_statuses=["ACTIVE"], update_resource=mock.ANY)
scenario._delete_server_with_fip.assert_called_once_with(
"foo_server", {"id": "foo_id", "ip": "foo_ip",
"is_floating": True},
force_delete=False)
scenario.add_output.assert_called_with(
complete={"chart_plugin": "TextArea",
"data": [
"foo_err"],
"title": "Script StdErr"})
@ddt.data(
{"image": "some_image",
"flavor": "m1.small", "username": "chuck norris"}
)
@mock.patch("rally.task.utils.get_from_manager")
@mock.patch("rally.task.utils.wait_for_status")
def test_create_share_and_access_from_vm_command_timeout(
self,
params,
mock_rally_task_utils_wait_for_status,
mock_rally_task_utils_get_from_manager):
scenario, fake_share = self.create_env(
shares.CreateShareAndAccessFromVM(self.context))
scenario._run_command.side_effect = exceptions.SSHTimeout()
self.assertRaises(exceptions.SSHTimeout,
scenario.run,
"foo_flavor", "foo_image", "foo_interpreter",
"foo_script", "foo_username")
scenario._delete_server_with_fip.assert_called_once_with(
"foo_server", self.ip, force_delete=False)
self.assertFalse(scenario.add_output.called)
scenario._delete_share.assert_called_once_with(fake_share)
@ddt.data(
{"image": "some_image",
"flavor": "m1.small", "username": "chuck norris"}
)
@mock.patch("rally.task.utils.get_from_manager")
@mock.patch("rally.task.utils.wait_for_status")
def test_create_share_and_access_from_vm_wait_timeout(
self,
params,
mock_rally_task_utils_wait_for_status,
mock_rally_task_utils_get_from_manager):
scenario, fake_share = self.create_env(
shares.CreateShareAndAccessFromVM(self.context))
mock_rally_task_utils_wait_for_status.side_effect = \
exceptions.TimeoutException(
resource_type="foo_resource",
resource_name="foo_name",
resource_id="foo_id",
desired_status="foo_desired_status",
resource_status="foo_resource_status",
timeout=2)
self.assertRaises(exceptions.TimeoutException,
scenario.run,
"foo_flavor", "foo_image", "foo_interpreter",
"foo_script", "foo_username")
scenario._delete_server_with_fip.assert_called_once_with(
"foo_server", self.ip, force_delete=False)
self.assertFalse(scenario.add_output.called)
scenario._delete_share.assert_called_once_with(fake_share)
@ddt.data(
{"output": (0, "", ""),
"expected": [{"complete": {"chart_plugin": "TextArea",
"data": [""],
"title": "Script StdOut"}}]},
{"output": (1, "x y z", "error message"),
"raises": exceptions.ScriptError},
{"output": (0, "[1, 2, 3, 4]", ""), "expected": []}
)
@ddt.unpack
def test_create_share_and_access_from_vm_add_output(self, output,
expected=None,
raises=None):
scenario, fake_share = self.create_env(
shares.CreateShareAndAccessFromVM(self.context))
scenario._run_command.return_value = output
kwargs = {"flavor": "foo_flavor",
"image": "foo_image",
"username": "foo_username",
"password": "foo_password",
"use_floating_ip": "use_fip",
"floating_network": "ext_network",
"force_delete": "foo_force"}
if raises:
self.assertRaises(raises, scenario.run, **kwargs)
self.assertFalse(scenario.add_output.called)
else:
scenario.run(**kwargs)
calls = [mock.call(**kw) for kw in expected]
scenario.add_output.assert_has_calls(calls, any_order=True)
scenario._create_share.assert_called_once_with(
share_proto="nfs", size=1)
scenario._delete_share.assert_called_once_with(fake_share)
scenario._allow_access_share.assert_called_once_with(
fake_share, "ip", "foo_ip", "rw")
scenario._export_location.assert_called_once_with(fake_share)
scenario._boot_server_with_fip.assert_called_once_with(
"foo_image", "foo_flavor", use_floating_ip="use_fip",
floating_network="ext_network", key_name="keypair_name",
userdata="#cloud-config\npackages:\n - nfs-common")
scenario._delete_server_with_fip.assert_called_once_with(
"foo_server",
{"id": "foo_id", "ip": "foo_ip", "is_floating": True},
force_delete="foo_force")
@ddt.data(
{},
{"detailed": True},

View File

@ -77,6 +77,12 @@ class ManilaScenarioTestCase(test.ScenarioTestCase):
self.mock_get_from_manager.mock.assert_called_once_with(
("error_deleting", ))
def test_export_location(self):
fake_share = mock.MagicMock()
fake_share.export_locations = "fake_location"
result = self.scenario._export_location(fake_share)
self.assertEqual(result, "fake_location")
@ddt.data(
{},
{"detailed": False, "search_opts": None},