Add the context custom_image
Add a new context with the name `custom_image'. This context is hidden and acts as the base for the contexts of the virtual machine boot from a custom prepared images. This is used for e.g. preparing an image with benchmark application preinstalled. If there is `admin' user then image is created using the first available user and then makes it public using the `admin' user. If no `admin' user is available then image is created for each user. Co-Authored-By: Tzanetos Balitsaris <tzabal@freebsd.org> Implements: blueprint benchmark-vms Change-Id: I63a6a0f0f8f1270014ef8322b8e1998b162eb760
This commit is contained in:
parent
8995027941
commit
415931a627
0
rally/benchmark/context/vm/__init__.py
Normal file
0
rally/benchmark/context/vm/__init__.py
Normal file
245
rally/benchmark/context/vm/custom_image.py
Normal file
245
rally/benchmark/context/vm/custom_image.py
Normal file
@ -0,0 +1,245 @@
|
||||
# Copyright 2015: Mirantis Inc.
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
from rally.benchmark.context import base
|
||||
from rally.benchmark.scenarios.nova import utils as nova_utils
|
||||
from rally.benchmark.scenarios.vm import vmtasks
|
||||
from rally.benchmark import types
|
||||
from rally.common import broker
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils
|
||||
from rally import consts
|
||||
from rally import osclients
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
@base.context(name="custom_image", order=500, hidden=True)
|
||||
class BaseCustomImageGenerator(base.Context):
|
||||
"""Base class for the contexts providing customized image with.
|
||||
|
||||
Every context class for the specific cusomization must implement the method
|
||||
`_customize_image` that is able to connect to the server using SSH
|
||||
and e.g. install applications inside it.
|
||||
|
||||
This is used e.g. to install the benchmark application using SSH
|
||||
access.
|
||||
|
||||
This base context class provides a way to prepare an image with
|
||||
custom preinstalled applications. Basically, this code boots a VM, calls
|
||||
the `_customize_image` and then snapshots the VM disk, removing the VM
|
||||
afterwards. The image UUID is stored in the user["custom_image"]["id"]
|
||||
and can be used afterwards by scenario.
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"flavor": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"floating_network": {
|
||||
"type": "string"
|
||||
},
|
||||
"internal_network": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
},
|
||||
"userdata": {
|
||||
"type": "string"
|
||||
},
|
||||
"workers": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
}
|
||||
},
|
||||
"required": ["image", "flavor"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"username": "root",
|
||||
"port": 22,
|
||||
"workers": 1
|
||||
}
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Enter context: `custom_image`"))
|
||||
def setup(self):
|
||||
"""Creates custom image(s) with preinstalled applications.
|
||||
|
||||
When admin is present creates one public image that is usable
|
||||
from all the tenants and users. Otherwise create one image
|
||||
per user and tenant.
|
||||
"""
|
||||
|
||||
if "admin" in self.context:
|
||||
# NOTE(pboldin): Create by first user and make it public by
|
||||
# the admin
|
||||
user = self.context["users"][0]
|
||||
tenant = self.context["tenants"][user["tenant_id"]]
|
||||
|
||||
nics = None
|
||||
if "networks" in tenant:
|
||||
nics = [{"net-id": tenant["networks"][0]["id"]}]
|
||||
|
||||
custom_image = self.create_one_image(user, nics=nics)
|
||||
self.make_image_public(custom_image)
|
||||
|
||||
for tenant in self.context["tenants"].values():
|
||||
tenant["custom_image"] = custom_image
|
||||
else:
|
||||
def publish(queue):
|
||||
users = self.context.get("users", [])
|
||||
for user, tenant_id in utils.iterate_per_tenants(users):
|
||||
queue.append((user, tenant_id))
|
||||
|
||||
def consume(cache, args):
|
||||
user, tenant_id = args
|
||||
tenant = self.context["tenants"][tenant_id]
|
||||
tenant["custom_image"] = self.create_one_image(user)
|
||||
|
||||
broker.run(publish, consume, self.config["workers"])
|
||||
|
||||
def create_one_image(self, user, **kwargs):
|
||||
"""Create one image for the user."""
|
||||
|
||||
clients = osclients.Clients(user["endpoint"])
|
||||
|
||||
image_id = types.ImageResourceType.transform(
|
||||
clients=clients, resource_config=self.config["image"])
|
||||
flavor_id = types.FlavorResourceType.transform(
|
||||
clients=clients, resource_config=self.config["flavor"])
|
||||
|
||||
vm_scenario = vmtasks.VMTasks(self.context, clients=clients)
|
||||
|
||||
server, fip = vm_scenario._boot_server_with_fip(
|
||||
name=vm_scenario._generate_random_name("rally_ctx_custom_image_"),
|
||||
image=image_id, flavor=flavor_id,
|
||||
floating_network=self.config.get("floating_network"),
|
||||
userdata=self.config.get("userdata"),
|
||||
key_name=user["keypair"]["name"],
|
||||
security_groups=[user["secgroup"]["name"]],
|
||||
**kwargs)
|
||||
|
||||
LOG.debug("Installing benchmark on %r %s", server, fip["ip"])
|
||||
self.customize_image(server, fip, user)
|
||||
|
||||
LOG.debug("Stopping server %r", server)
|
||||
vm_scenario._stop_server(server)
|
||||
|
||||
LOG.debug("Creating snapshot for %r", server)
|
||||
custom_image = vm_scenario._create_image(server).to_dict()
|
||||
|
||||
vm_scenario._delete_server_with_fip(server, fip)
|
||||
|
||||
return custom_image
|
||||
|
||||
def make_image_public(self, custom_image):
|
||||
"""Make the image available publicly."""
|
||||
|
||||
admin_clients = osclients.Clients(self.context["admin"]["endpoint"])
|
||||
|
||||
LOG.debug("Making image %r public", custom_image["id"])
|
||||
admin_clients.glance().images.get(
|
||||
custom_image["id"]).update(is_public=True)
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Exit context: `custom_image`"))
|
||||
def cleanup(self):
|
||||
"""Delete created custom image(s)."""
|
||||
|
||||
if "admin" in self.context:
|
||||
user = self.context["users"][0]
|
||||
tenant = self.context["tenants"][user["tenant_id"]]
|
||||
if "custom_image" in tenant:
|
||||
self.delete_one_image(user, tenant["custom_image"])
|
||||
tenant.pop("custom_image")
|
||||
else:
|
||||
def publish(queue):
|
||||
users = self.context.get("users", [])
|
||||
for user, tenant_id in utils.iterate_per_tenants(users):
|
||||
queue.append((user, tenant_id))
|
||||
|
||||
def consume(cache, args):
|
||||
user, tenant_id = args
|
||||
tenant = self.context["tenants"][tenant_id]
|
||||
if "custom_image" in tenant:
|
||||
self.delete_one_image(user, tenant["custom_image"])
|
||||
tenant.pop("custom_image")
|
||||
|
||||
broker.run(publish, consume, self.config["workers"])
|
||||
|
||||
def delete_one_image(self, user, custom_image):
|
||||
"""Delete the image created for the user and tenant."""
|
||||
|
||||
clients = osclients.Clients(user["endpoint"])
|
||||
|
||||
nova_scenario = nova_utils.NovaScenario(
|
||||
context=self.context, clients=clients)
|
||||
|
||||
with logging.ExceptionLogger(
|
||||
LOG, _("Unable to delete image %s") % custom_image["id"]):
|
||||
|
||||
custom_image = nova_scenario.clients("nova").images.get(
|
||||
custom_image["id"])
|
||||
nova_scenario._delete_image(custom_image)
|
||||
|
||||
@utils.log_task_wrapper(LOG.info,
|
||||
_("Custom image context: customizing"))
|
||||
def customize_image(self, server, fip, user):
|
||||
return self._customize_image(server, fip, user)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _customize_image(self, server, fip, user):
|
||||
"""Override this method with one that customizes image.
|
||||
|
||||
Basically, code can simply call `VMScenario._run_command` function
|
||||
specifying an installation script and interpreter. This script will
|
||||
be then executed using SSH.
|
||||
|
||||
:param server: nova.Server instance
|
||||
:param fip: dict with Floating IP details
|
||||
:param user: user who started a VM instance. Used to extract keypair
|
||||
"""
|
||||
pass
|
@ -104,8 +104,8 @@ class NovaScenario(base.Scenario):
|
||||
kwargs["security_groups"].append(secgroup["name"])
|
||||
|
||||
if auto_assign_nic and not kwargs.get("nics", False):
|
||||
nets = [net["id"]
|
||||
for net in self.context["tenant"].get("networks", [])]
|
||||
nets = [net["id"] for net in
|
||||
self.context.get("tenant", {}).get("networks", [])]
|
||||
if nets:
|
||||
# NOTE(amaretskiy): Balance servers among networks:
|
||||
# divmod(iteration % tenants_num, nets_num)[1]
|
||||
|
@ -17,12 +17,15 @@ import subprocess
|
||||
import sys
|
||||
|
||||
import netaddr
|
||||
import six
|
||||
|
||||
from rally.benchmark.scenarios import base
|
||||
from rally.benchmark import utils as bench_utils
|
||||
from rally.benchmark.wrappers import network as network_wrapper
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import sshutils
|
||||
from rally import exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -33,15 +36,29 @@ class VMScenario(base.Scenario):
|
||||
VM scenarios are scenarios executed inside some launched VM instance.
|
||||
"""
|
||||
|
||||
@base.atomic_action_timer("vm.run_command")
|
||||
def _run_action(self, ssh, interpreter, script):
|
||||
@base.atomic_action_timer("vm.run_command_over_ssh")
|
||||
def _run_command_over_ssh(self, ssh, interpreter, script):
|
||||
"""Run command inside an instance.
|
||||
|
||||
This is a separate function so that only script execution is timed.
|
||||
|
||||
:param ssh: A SSHClient instance.
|
||||
:param interpreter: The interpreter that will be used to execute
|
||||
the script.
|
||||
:param script: Path to the script file or its content in a StringIO.
|
||||
|
||||
:returns: tuple (exit_status, stdout, stderr)
|
||||
"""
|
||||
return ssh.execute(interpreter, stdin=open(script, "rb"))
|
||||
if isinstance(script, six.string_types):
|
||||
stdin = open(script, "rb")
|
||||
elif isinstance(script, six.moves.StringIO):
|
||||
stdin = script
|
||||
else:
|
||||
raise exceptions.ScriptError(
|
||||
"Either file path or StringIO expected, given %s" %
|
||||
type(script).__name__)
|
||||
|
||||
return ssh.execute(interpreter, stdin=stdin)
|
||||
|
||||
def _get_netwrap(self):
|
||||
if not hasattr(self, "_netwrap"):
|
||||
@ -106,20 +123,19 @@ class VMScenario(base.Scenario):
|
||||
)
|
||||
|
||||
def _run_command(self, server_ip, port, username, password,
|
||||
interpreter, script):
|
||||
interpreter, script, pkey=None):
|
||||
"""Run command via SSH on server.
|
||||
|
||||
Create SSH connection for server, wait for server to become
|
||||
available (there is a delay between server being set to ACTIVE
|
||||
and sshd being available). Then call run_action to actually
|
||||
and sshd being available). Then call run_command_over_ssh to actually
|
||||
execute the command.
|
||||
"""
|
||||
pkey = pkey if pkey else self.context["user"]["keypair"]["private"]
|
||||
ssh = sshutils.SSH(username, server_ip, port=port,
|
||||
pkey=self.context["user"]["keypair"]["private"],
|
||||
password=password)
|
||||
|
||||
pkey=pkey, password=password)
|
||||
self._wait_for_ssh(ssh)
|
||||
return self._run_action(ssh, interpreter, script)
|
||||
return self._run_command_over_ssh(ssh, interpreter, script)
|
||||
|
||||
@staticmethod
|
||||
def _ping_ip_address(host, should_succeed=True):
|
||||
|
0
tests/unit/benchmark/context/vm/__init__.py
Normal file
0
tests/unit/benchmark/context/vm/__init__.py
Normal file
227
tests/unit/benchmark/context/vm/test_custom_image.py
Normal file
227
tests/unit/benchmark/context/vm/test_custom_image.py
Normal file
@ -0,0 +1,227 @@
|
||||
# Copyright 2015: Mirantis Inc.
|
||||
# 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.
|
||||
|
||||
"""Tests for the Benchmark VM image context."""
|
||||
|
||||
import mock
|
||||
|
||||
from rally.benchmark.context.vm import custom_image
|
||||
from tests.unit import test
|
||||
|
||||
BASE = "rally.benchmark.context.vm.custom_image"
|
||||
|
||||
|
||||
class TestImageGenerator(custom_image.BaseCustomImageGenerator):
|
||||
def _customize_image(self, *args):
|
||||
pass
|
||||
|
||||
|
||||
class BaseCustomImageContextVMTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(BaseCustomImageContextVMTestCase, self).setUp()
|
||||
|
||||
self.context = {
|
||||
"task": mock.MagicMock(),
|
||||
"config": {
|
||||
"custom_image": {
|
||||
"image": {"name": "image"},
|
||||
"flavor": {"name": "flavor"},
|
||||
"username": "fedora",
|
||||
"floating_network": "floating",
|
||||
"port": 1022,
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": "endpoint",
|
||||
},
|
||||
"users": [
|
||||
{"tenant_id": "tenant_id0"},
|
||||
{"tenant_id": "tenant_id1"},
|
||||
{"tenant_id": "tenant_id2"}
|
||||
],
|
||||
"tenants": {
|
||||
"tenant_id0": {},
|
||||
"tenant_id1": {},
|
||||
"tenant_id2": {}
|
||||
}
|
||||
}
|
||||
|
||||
@mock.patch("%s.vmtasks.VMTasks" % BASE)
|
||||
@mock.patch("%s.osclients.Clients" % BASE)
|
||||
@mock.patch("%s.types.ImageResourceType.transform" % BASE,
|
||||
return_value="image")
|
||||
@mock.patch("%s.types.FlavorResourceType.transform" % BASE,
|
||||
return_value="flavor")
|
||||
def test_create_one_image(self, mock_flavor_transform,
|
||||
mock_image_transform, mock_osclients,
|
||||
mock_vmtasks):
|
||||
fip = {"ip": "foo_ip"}
|
||||
fake_server = mock.Mock()
|
||||
|
||||
fake_image = mock.MagicMock(
|
||||
to_dict=mock.MagicMock(return_value={"id": "image"}))
|
||||
|
||||
mock_vm_scenario = mock_vmtasks.return_value = mock.MagicMock(
|
||||
_create_image=mock.MagicMock(return_value=fake_image),
|
||||
_boot_server_with_fip=mock.MagicMock(
|
||||
return_value=(fake_server, fip)),
|
||||
_generate_random_name=mock.MagicMock(return_value="foo_name"),
|
||||
)
|
||||
|
||||
generator_ctx = TestImageGenerator(self.context)
|
||||
generator_ctx._customize_image = mock.MagicMock()
|
||||
|
||||
user = {
|
||||
"endpoint": "endpoint",
|
||||
"keypair": {"name": "keypair_name"},
|
||||
"secgroup": {"name": "secgroup_name"}
|
||||
}
|
||||
|
||||
custom_image = generator_ctx.create_one_image(user,
|
||||
foo_arg="foo_value")
|
||||
|
||||
mock_flavor_transform.assert_called_once_with(
|
||||
clients=mock_osclients.return_value,
|
||||
resource_config={"name": "flavor"})
|
||||
mock_image_transform.assert_called_once_with(
|
||||
clients=mock_osclients.return_value,
|
||||
resource_config={"name": "image"})
|
||||
mock_vmtasks.assert_called_once_with(
|
||||
self.context, clients=mock_osclients.return_value)
|
||||
|
||||
mock_vm_scenario._boot_server_with_fip.assert_called_once_with(
|
||||
name="foo_name", image="image", flavor="flavor",
|
||||
floating_network="floating", key_name="keypair_name",
|
||||
security_groups=["secgroup_name"],
|
||||
userdata=None, foo_arg="foo_value")
|
||||
|
||||
mock_vm_scenario._stop_server.assert_called_once_with(fake_server)
|
||||
|
||||
generator_ctx._customize_image.assert_called_once_with(
|
||||
fake_server, fip, user)
|
||||
|
||||
mock_vm_scenario._create_image.assert_called_once_with(fake_server)
|
||||
|
||||
mock_vm_scenario._delete_server_with_fip.assert_called_once_with(
|
||||
fake_server, fip)
|
||||
|
||||
self.assertEqual({"id": "image"}, custom_image)
|
||||
|
||||
@mock.patch("%s.osclients.Clients" % BASE)
|
||||
def test_make_image_public(self, mock_osclients):
|
||||
fc = mock.MagicMock()
|
||||
mock_osclients.return_value = fc
|
||||
|
||||
generator_ctx = TestImageGenerator(self.context)
|
||||
custom_image = {"id": "image"}
|
||||
|
||||
generator_ctx.make_image_public(custom_image=custom_image)
|
||||
|
||||
mock_osclients.assert_called_once_with(
|
||||
self.context["admin"]["endpoint"])
|
||||
|
||||
fc.glance.assert_called_once_with()
|
||||
fc.glance.return_value.images.get.assert_called_once_with("image")
|
||||
(fc.glance.return_value.images.get.
|
||||
return_value.update.assert_called_once_with(is_public=True))
|
||||
|
||||
@mock.patch("%s.nova_utils.NovaScenario" % BASE)
|
||||
@mock.patch("%s.osclients.Clients" % BASE)
|
||||
def test_delete_one_image(self, mock_osclients, mock_nova_scenario):
|
||||
nova_scenario = mock_nova_scenario.return_value = mock.MagicMock()
|
||||
nova_client = nova_scenario.clients.return_value
|
||||
nova_client.images.get.return_value = "image_obj"
|
||||
|
||||
generator_ctx = TestImageGenerator(self.context)
|
||||
|
||||
user = {"endpoint": "endpoint", "keypair": {"name": "keypair_name"}}
|
||||
custom_image = {"id": "image"}
|
||||
|
||||
generator_ctx.delete_one_image(user, custom_image)
|
||||
|
||||
mock_nova_scenario.assert_called_once_with(
|
||||
context=self.context, clients=mock_osclients.return_value)
|
||||
|
||||
nova_scenario.clients.assert_called_once_with("nova")
|
||||
nova_client.images.get.assert_called_once_with("image")
|
||||
nova_scenario._delete_image.assert_called_once_with("image_obj")
|
||||
|
||||
def test_setup_admin(self):
|
||||
self.context["tenants"]["tenant_id0"]["networks"] = [
|
||||
{"id": "network_id"}]
|
||||
|
||||
generator_ctx = TestImageGenerator(self.context)
|
||||
|
||||
generator_ctx.create_one_image = mock.Mock(
|
||||
return_value="custom_image")
|
||||
generator_ctx.make_image_public = mock.Mock()
|
||||
|
||||
generator_ctx.setup()
|
||||
|
||||
generator_ctx.create_one_image.assert_called_once_with(
|
||||
self.context["users"][0], nics=[{"net-id": "network_id"}])
|
||||
generator_ctx.make_image_public.assert_called_once_with(
|
||||
"custom_image")
|
||||
|
||||
def test_cleanup_admin(self):
|
||||
tenant = self.context["tenants"]["tenant_id0"]
|
||||
custom_image = tenant["custom_image"] = {"id": "image"}
|
||||
|
||||
generator_ctx = TestImageGenerator(self.context)
|
||||
|
||||
generator_ctx.delete_one_image = mock.Mock()
|
||||
|
||||
generator_ctx.cleanup()
|
||||
|
||||
generator_ctx.delete_one_image.assert_called_once_with(
|
||||
self.context["users"][0], custom_image)
|
||||
|
||||
def test_setup(self):
|
||||
self.context.pop("admin")
|
||||
|
||||
generator_ctx = TestImageGenerator(self.context)
|
||||
|
||||
generator_ctx.create_one_image = mock.Mock(
|
||||
side_effect=["custom_image0", "custom_image1", "custom_image2"])
|
||||
|
||||
generator_ctx.setup()
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call(user) for user in self.context["users"]],
|
||||
generator_ctx.create_one_image.mock_calls)
|
||||
|
||||
for i in range(3):
|
||||
self.assertEqual(
|
||||
"custom_image%d" % i,
|
||||
self.context["tenants"]["tenant_id%d" % i]["custom_image"]
|
||||
)
|
||||
|
||||
def test_cleanup(self):
|
||||
self.context.pop("admin")
|
||||
|
||||
for i in range(3):
|
||||
self.context["tenants"]["tenant_id%d" % i]["custom_image"] = {
|
||||
"id": "custom_image%d" % i}
|
||||
|
||||
generator_ctx = TestImageGenerator(self.context)
|
||||
generator_ctx.delete_one_image = mock.Mock()
|
||||
|
||||
generator_ctx.cleanup()
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call(self.context["users"][i],
|
||||
{"id": "custom_image%d" % i}) for i in range(3)],
|
||||
generator_ctx.delete_one_image.mock_calls)
|
@ -16,10 +16,13 @@
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
import mock
|
||||
from oslotest import mockpatch
|
||||
import six
|
||||
|
||||
from rally.benchmark.scenarios.vm import utils
|
||||
from rally import exceptions
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
@ -36,13 +39,27 @@ class VMScenarioTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("%s.open" % VMTASKS_UTILS,
|
||||
side_effect=mock.mock_open(), create=True)
|
||||
def test__run_action(self, mock_open):
|
||||
def test__run_command_over_ssh(self, mock_open):
|
||||
mock_ssh = mock.MagicMock()
|
||||
vm_scenario = utils.VMScenario()
|
||||
vm_scenario._run_action(mock_ssh, "interpreter", "script")
|
||||
vm_scenario._run_command_over_ssh(mock_ssh, "interpreter", "script")
|
||||
mock_ssh.execute.assert_called_once_with("interpreter",
|
||||
stdin=mock_open.side_effect())
|
||||
|
||||
def test__run_command_over_ssh_stringio(self):
|
||||
mock_ssh = mock.MagicMock()
|
||||
vm_scenario = utils.VMScenario()
|
||||
script = six.moves.StringIO("script")
|
||||
vm_scenario._run_command_over_ssh(mock_ssh, "interpreter", script)
|
||||
mock_ssh.execute.assert_called_once_with("interpreter",
|
||||
stdin=script)
|
||||
|
||||
def test__run_command_over_ssh_fails(self):
|
||||
vm_scenario = utils.VMScenario()
|
||||
self.assertRaises(exceptions.ScriptError,
|
||||
vm_scenario._run_command_over_ssh,
|
||||
None, "interpreter", 10)
|
||||
|
||||
def test__wait_for_ssh(self):
|
||||
ssh = mock.MagicMock()
|
||||
vm_scenario = utils.VMScenario()
|
||||
@ -58,9 +75,9 @@ class VMScenarioTestCase(test.TestCase):
|
||||
is_ready=mock__ping,
|
||||
timeout=120)
|
||||
|
||||
@mock.patch(VMTASKS_UTILS + ".VMScenario._run_action")
|
||||
@mock.patch(VMTASKS_UTILS + ".VMScenario._run_command_over_ssh")
|
||||
@mock.patch("rally.common.sshutils.SSH")
|
||||
def test__run_command(self, mock_ssh_class, mock_run_action):
|
||||
def test__run_command(self, mock_ssh_class, mock_run_command_over_ssh):
|
||||
mock_ssh_instance = mock.MagicMock()
|
||||
mock_ssh_class.return_value = mock_ssh_instance
|
||||
|
||||
@ -73,8 +90,8 @@ class VMScenarioTestCase(test.TestCase):
|
||||
pkey="ssh",
|
||||
password="password")
|
||||
mock_ssh_instance.wait.assert_called_once_with()
|
||||
mock_run_action.assert_called_once_with(mock_ssh_instance,
|
||||
"int", "script")
|
||||
mock_run_command_over_ssh.assert_called_once_with(
|
||||
mock_ssh_instance, "int", "script")
|
||||
|
||||
@mock.patch(VMTASKS_UTILS + ".sys")
|
||||
@mock.patch("subprocess.Popen")
|
||||
|
Loading…
Reference in New Issue
Block a user