add manila+VM test
create NFS share and access it from VM Change-Id: I588251b032ce3f283626b4ca8032b517920d7e83
This commit is contained in:
parent
771c922af5
commit
93ee1c074f
@ -14,16 +14,22 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from rally.common import logging
|
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.task import validation
|
||||||
|
|
||||||
from rally_openstack.common import consts
|
from rally_openstack.common import consts
|
||||||
from rally_openstack.task.contexts.manila import consts as manila_consts
|
from rally_openstack.task.contexts.manila import consts as manila_consts
|
||||||
from rally_openstack.task import scenario
|
from rally_openstack.task import scenario
|
||||||
from rally_openstack.task.scenarios.manila import utils
|
from rally_openstack.task.scenarios.manila import utils
|
||||||
|
from rally_openstack.task.scenarios.vm import utils as vm_utils
|
||||||
|
|
||||||
|
|
||||||
"""Scenarios for Manila shares."""
|
"""Scenarios for Manila shares."""
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@validation.add("enum", param_name="share_proto",
|
@validation.add("enum", param_name="share_proto",
|
||||||
values=["NFS", "CIFS", "GLUSTERFS", "HDFS", "CEPHFS"],
|
values=["NFS", "CIFS", "GLUSTERFS", "HDFS", "CEPHFS"],
|
||||||
@ -56,6 +62,113 @@ class CreateAndDeleteShare(utils.ManilaScenario):
|
|||||||
self.sleep_between(min_sleep, max_sleep)
|
self.sleep_between(min_sleep, max_sleep)
|
||||||
self._delete_share(share)
|
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_services", services=[consts.Service.MANILA])
|
||||||
@validation.add("required_platform", platform="openstack", users=True)
|
@validation.add("required_platform", platform="openstack", users=True)
|
||||||
@ -242,9 +355,9 @@ class ListShareServers(utils.ManilaScenario):
|
|||||||
self._list_share_servers(search_opts=search_opts)
|
self._list_share_servers(search_opts=search_opts)
|
||||||
|
|
||||||
|
|
||||||
@validation.add("enum", param_name="share_proto", values=["nfs", "cephfs",
|
@validation.add("enum", param_name="share_proto",
|
||||||
"cifs", "glusterfs", "hdfs"], missed=False,
|
values=["nfs", "cephfs", "cifs", "glusterfs", "hdfs"],
|
||||||
case_insensitive=True)
|
missed=False, case_insensitive=True)
|
||||||
@validation.add("required_services", services=[consts.Service.MANILA])
|
@validation.add("required_services", services=[consts.Service.MANILA])
|
||||||
@validation.add("required_platform", platform="openstack", users=True)
|
@validation.add("required_platform", platform="openstack", users=True)
|
||||||
@scenario.configure(
|
@scenario.configure(
|
||||||
|
@ -86,6 +86,14 @@ class ManilaScenario(scenario.OpenStackScenario):
|
|||||||
timeout=CONF.openstack.manila_share_delete_timeout,
|
timeout=CONF.openstack.manila_share_delete_timeout,
|
||||||
check_interval=CONF.openstack.manila_share_delete_poll_interval)
|
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):
|
def _get_access_from_share(self, share, access_id):
|
||||||
"""Get access from share
|
"""Get access from share
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
@ -17,6 +17,7 @@ from unittest import mock
|
|||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
|
|
||||||
|
from rally import exceptions
|
||||||
from rally_openstack.task.scenarios.manila import shares
|
from rally_openstack.task.scenarios.manila import shares
|
||||||
from tests.unit import test
|
from tests.unit import test
|
||||||
|
|
||||||
@ -42,6 +43,164 @@ class ManilaSharesTestCase(test.ScenarioTestCase):
|
|||||||
scenario.sleep_between.assert_called_once_with(3, 4)
|
scenario.sleep_between.assert_called_once_with(3, 4)
|
||||||
scenario._delete_share.assert_called_once_with(fake_share)
|
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(
|
@ddt.data(
|
||||||
{},
|
{},
|
||||||
{"detailed": True},
|
{"detailed": True},
|
||||||
|
@ -77,6 +77,12 @@ class ManilaScenarioTestCase(test.ScenarioTestCase):
|
|||||||
self.mock_get_from_manager.mock.assert_called_once_with(
|
self.mock_get_from_manager.mock.assert_called_once_with(
|
||||||
("error_deleting", ))
|
("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(
|
@ddt.data(
|
||||||
{},
|
{},
|
||||||
{"detailed": False, "search_opts": None},
|
{"detailed": False, "search_opts": None},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user