Add nova floating ip management in VM scenario

This patch allows VM runcommand scenario attach a floating ip
to the instance to perform ssh connection.

Blueprint: benchmark-scenarios-vm-floating-ip

Change-Id: I2e77d6be34a64d5feb9d3ecc5e35c8a712b10bed
This commit is contained in:
Bruno Semperlotti 2014-04-16 10:59:22 -07:00
parent 044abcbd8f
commit 3d8b423db3
16 changed files with 698 additions and 73 deletions

View File

@ -8,6 +8,9 @@
"image": { "image": {
"name": "cirros-0.3.1-x86_64-uec" "name": "cirros-0.3.1-x86_64-uec"
}, },
"fixed_network": "private",
"floating_network": "public",
"use_floatingip": true,
"script": "doc/samples/support/instance_dd_test.sh", "script": "doc/samples/support/instance_dd_test.sh",
"interpreter": "/bin/sh", "interpreter": "/bin/sh",
"username": "cirros" "username": "cirros"

View File

@ -6,6 +6,9 @@
name: "m1.nano" name: "m1.nano"
image: image:
name: "cirros-0.3.1-x86_64-uec" name: "cirros-0.3.1-x86_64-uec"
fixed_network: "private"
floating_network: "public"
use_floatingip: true
script: "doc/samples/support/instance_dd_test.sh" script: "doc/samples/support/instance_dd_test.sh"
interpreter: "/bin/sh" interpreter: "/bin/sh"
username: "cirros" username: "cirros"

View File

@ -102,6 +102,24 @@
type: "constant" type: "constant"
times: 100 times: 100
concurrency: 10 concurrency: 10
VMTasks.boot_runcommand_delete:
-
args:
flavor:
name: "m1.tiny"
image:
name: "cirros-0.3.2-x86_64-uec"
fixed_network: "private"
floating_network: "public"
use_floatingip: true
script: "/home/jenkins/.rally/extra/instance_dd_test.sh"
interpreter: "/bin/sh"
username: "cirros"
runner:
type: "constant"
times: 6
concurrency: 3
context: context:
users: users:
tenants: 1 tenants: 1

View File

@ -321,3 +321,44 @@
users: users:
tenants: 3 tenants: 3
users_per_tenant: 5 users_per_tenant: 5
VMTasks.boot_runcommand_delete:
-
args:
flavor:
name: "m1.tiny"
image:
name: "cirros-0.3.2-x86_64-uec"
fixed_network: "private"
floating_network: "public"
use_floatingip: true
script: "/home/jenkins/.rally/extra/instance_dd_test.sh"
interpreter: "/bin/sh"
username: "cirros"
runner:
type: "constant"
times: 6
concurrency: 3
context:
users:
tenants: 1
users_per_tenant: 1
-
args:
flavor:
name: "m1.tiny"
image:
name: "cirros-0.3.2-x86_64-uec"
fixed_network: "private"
use_floatingip: false
script: "/home/jenkins/.rally/extra/instance_dd_test.sh"
interpreter: "/bin/sh"
username: "cirros"
runner:
type: "constant"
times: 6
concurrency: 3
context:
users:
tenants: 1
users_per_tenant: 1

View File

@ -21,6 +21,7 @@ from rally.benchmark.scenarios import base
from rally.benchmark.scenarios import utils as scenario_utils from rally.benchmark.scenarios import utils as scenario_utils
from rally.benchmark import utils as bench_utils from rally.benchmark import utils as bench_utils
nova_benchmark_opts = [] nova_benchmark_opts = []
option_names_and_defaults = [ option_names_and_defaults = [
# action, prepoll delay, timeout, poll interval # action, prepoll delay, timeout, poll interval
@ -93,6 +94,7 @@ class NovaScenario(base.Scenario):
server = self.clients("nova").servers.create(server_name, image_id, server = self.clients("nova").servers.create(server_name, image_id,
flavor_id, **kwargs) flavor_id, **kwargs)
time.sleep(CONF.benchmark.nova_server_boot_prepoll_delay) time.sleep(CONF.benchmark.nova_server_boot_prepoll_delay)
server = bench_utils.wait_for( server = bench_utils.wait_for(
server, server,
@ -312,3 +314,86 @@ class NovaScenario(base.Scenario):
check_interval=CONF.benchmark.nova_server_boot_poll_interval check_interval=CONF.benchmark.nova_server_boot_poll_interval
) for server in servers] ) for server in servers]
return servers return servers
@scenario_utils.atomic_action_timer('nova.list_floating_ip_pools')
def _list_floating_ip_pools(self):
"""Returns user floating ip pools list."""
return self.clients("nova").floating_ip_pools.list()
@scenario_utils.atomic_action_timer('nova.list_floating_ips')
def _list_floating_ips(self):
"""Returns user floating ips list."""
return self.clients("nova").floating_ips.list()
@scenario_utils.atomic_action_timer('nova.create_floating_ip')
def _create_floating_ip(self, pool):
"""Create (allocate) a floating ip from the given pool
:param pool: Name of the floating ip pool or external network
:returns: The created floating ip
"""
return self.clients("nova").floating_ips.create(pool)
@scenario_utils.atomic_action_timer('nova.delete_floating_ip')
def _delete_floating_ip(self, floating_ip):
"""Delete (deallocate) a floating ip for a tenant
:param floating_ip: The floating ip address to delete.
"""
self.clients("nova").floating_ips.delete(floating_ip)
bench_utils.wait_for_delete(
floating_ip,
update_resource=bench_utils.get_from_manager()
)
@scenario_utils.atomic_action_timer('nova.associate_floating_ip')
def _associate_floating_ip(self, server, address, fixed_address=None):
"""Add floating IP to an instance
:param server: The :class:`Server` to add an IP to.
:param address: The ip address or FloatingIP to add to the instance
:param fixed_address: The fixedIP address the FloatingIP is to be
associated with (optional)
"""
server.add_floating_ip(address, fixed_address=fixed_address)
bench_utils.wait_for(
server,
is_ready=self.check_ip_address(address),
update_resource=bench_utils.get_from_manager()
)
# Update server data
server.addresses = server.manager.get(server.id).addresses
@scenario_utils.atomic_action_timer('nova.dissociate_floating_ip')
def _dissociate_floating_ip(self, server, address):
"""Remove floating IP from an instance
:param server: The :class:`Server` to add an IP to.
:param address: The ip address or FloatingIP to remove
"""
server.remove_floating_ip(address)
bench_utils.wait_for(
server,
is_ready=self.check_ip_address(address, must_exist=False),
update_resource=bench_utils.get_from_manager()
)
# Update server data
server.addresses = server.manager.get(server.id).addresses
@staticmethod
def check_ip_address(address, must_exist=True):
ip_to_check = getattr(address, "ip", address)
def _check_addr(resource):
for network, addr_list in resource.addresses.items():
for addr in addr_list:
if ip_to_check == addr["addr"]:
return must_exist
return not must_exist
return _check_addr
@scenario_utils.atomic_action_timer('nova.list_networks')
def _list_networks(self):
"""Returns user networks list."""
return self.clients("nova").networks.list()

View File

@ -14,8 +14,11 @@
# under the License. # under the License.
import subprocess
from rally.benchmark.scenarios import base from rally.benchmark.scenarios import base
from rally.benchmark.scenarios import utils as scenario_utils from rally.benchmark.scenarios import utils as scenario_utils
from rally.benchmark import utils as bench_utils
from rally import sshutils from rally import sshutils
@ -29,12 +32,19 @@ class VMScenario(base.Scenario):
""" """
return ssh.execute(interpreter, stdin=open(script, "rb")) return ssh.execute(interpreter, stdin=open(script, "rb"))
@scenario_utils.atomic_action_timer('vm.wait_for_network') @scenario_utils.atomic_action_timer('vm.wait_for_ssh')
def wait_for_network(self, ssh): def wait_for_ssh(self, ssh):
ssh.wait() ssh.wait()
def run_command(self, server, username, network, port, ip_version, @scenario_utils.atomic_action_timer('vm.wait_for_ping')
interpreter, script): def wait_for_ping(self, server_ip):
bench_utils.wait_for(
server_ip,
is_ready=self.ping_ip_address,
timeout=120
)
def run_command(self, server_ip, port, username, interpreter, script):
"""Run command via SSH on server. """Run command via SSH on server.
Create SSH connection for server, wait for server to become Create SSH connection for server, wait for server to become
@ -42,21 +52,38 @@ class VMScenario(base.Scenario):
and sshd being available). Then call __run_command to actually and sshd being available). Then call __run_command to actually
execute the command. execute the command.
""" """
self.wait_for_ping(server_ip)
if network not in server.addresses:
raise ValueError(
"Can't find cloud network %(network)s, so cannot boot "
"instance for Rally scenario boot-runcommand-delete. "
"Available networks: %(networks)s" % (
dict(network=network,
networks=server.addresses.keys()
)
)
)
server_ip = [ip for ip in server.addresses[network] if
ip["version"] == ip_version][0]["addr"]
ssh = sshutils.SSH(username, server_ip, port=port, ssh = sshutils.SSH(username, server_ip, port=port,
pkey=self.context()["user"]["keypair"]["private"]) pkey=self.context()["user"]["keypair"]["private"])
self.wait_for_network(ssh) self.wait_for_ssh(ssh)
return self.run_action(ssh, interpreter, script) return self.run_action(ssh, interpreter, script)
@staticmethod
def check_network(server, network):
"""Check if a server is attached to the specified network.
:param server: The server object to consider
:param network: The name of the network to search for.
:raises: `ValueError` if server is not attached to network.
"""
if network not in server.addresses:
raise ValueError(
"Server %(server_name)s is not attached to"
" network %(network)s. "
"Attached networks are: %(networks)s" % {
"server_name": server.name,
"network": network,
"networks": server.addresses.keys()
}
)
@staticmethod
def ping_ip_address(host, should_succeed=True):
cmd = ['ping', '-c1', '-w1', host]
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
proc.wait()
return (proc.returncode == 0) == should_succeed

View File

@ -20,11 +20,7 @@ from rally.benchmark.scenarios.nova import utils as nova_utils
from rally.benchmark.scenarios.vm import utils as vm_utils from rally.benchmark.scenarios.vm import utils as vm_utils
from rally.benchmark import types as types from rally.benchmark import types as types
from rally.benchmark import validation from rally.benchmark import validation
from rally.openstack.common.gettextutils import _ # noqa from rally import exceptions
from rally.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class VMTasks(nova_utils.NovaScenario, vm_utils.VMScenario): class VMTasks(nova_utils.NovaScenario, vm_utils.VMScenario):
@ -38,44 +34,100 @@ class VMTasks(nova_utils.NovaScenario, vm_utils.VMScenario):
@validation.add(validation.file_exists("script")) @validation.add(validation.file_exists("script"))
@validation.add(validation.number("port", minval=1, maxval=65535, @validation.add(validation.number("port", minval=1, maxval=65535,
nullable=True, integer_only=True)) nullable=True, integer_only=True))
@validation.add(validation.external_network_exists("floating_network",
"use_floatingip"))
@base.scenario(context={"cleanup": ["nova"], @base.scenario(context={"cleanup": ["nova"],
"keypair": {}, "allow_ssh": {}}) "keypair": {}, "allow_ssh": {}})
def boot_runcommand_delete(self, image, flavor, def boot_runcommand_delete(self, image, flavor,
script, interpreter, network='private', script, interpreter, username,
username='ubuntu', ip_version=4, fixed_network="private",
port=22, **kwargs): floating_network="public",
ip_version=4, port=22,
use_floatingip=True, **kwargs):
"""Boot server, run a script that outputs JSON, delete server. """Boot server, run a script that outputs JSON, delete server.
Parameters: :param script: script to run on the server, must output JSON mapping
script: script to run on the server, must output JSON mapping metric metric names to values. See sample script below.
names to values. See sample script below. :param interpreter: The shell interpreter to use when running script
network: Network to choose address to connect to instance from :param username: User to SSH to instance as
username: User to SSH to instance as :param fixed_network: Network where instance is part of
ip_version: Version of ip protocol to use for connection :param floating_network: External network used to get floating ip from
:param ip_version: Version of ip protocol to use for connection
:param port: Port to use for SSH connection
:param use_floatingip: Whether to associate a floating ip for
connection
returns: Dictionary containing two keys, data and errors. Data is JSON :returns: Dictionary containing two keys, data and errors. Data is JSON
data output by the script. Errors is raw data from the data output by the script. Errors is raw data from the
script's standard error stream. script's standard error stream.
Example Script in doc/samples/support/instance_dd_test.sh Example Script in doc/samples/support/instance_dd_test.sh
""" """
server = self._boot_server( server = None
self._generate_random_name("rally_novaserver_"), floating_ip = None
image, flavor, key_name='rally_ssh_key', **kwargs)
code, out, err = self.run_command(server, username, network, port,
ip_version, interpreter, script)
if code:
LOG.error(_("Error running script on instance via SSH. "
"Error: %s") % err)
try: try:
out = json.loads(out) server = self._boot_server(
except ValueError: self._generate_random_name("rally_novaserver_"),
LOG.warning(_("Script %s did not output valid JSON.") % script) image, flavor, key_name='rally_ssh_key', **kwargs)
self.check_network(server, fixed_network)
fixed_ip = [ip for ip in server.addresses[fixed_network] if
ip["version"] == ip_version][0]["addr"]
if use_floatingip:
floating_ip = self._create_floating_ip(floating_network)
self._associate_floating_ip(server, floating_ip)
server_ip = floating_ip.ip
else:
server_ip = fixed_ip
code, out, err = self.run_command(server_ip, port,
username, interpreter, script)
if code:
raise exceptions.ScriptError(
"Error running script %(script)s."
"Error %(code)s: %(error)s" % {
"script": script,
"code": code,
"error": err
})
try:
out = json.loads(out)
except ValueError as e:
raise exceptions.ScriptError(
"Script %(script)s did not output valid JSON: %(error)s" %
{
"script": script,
"error": str(e)
}
)
# Always try to free resources
finally:
if use_floatingip:
self._release_server_floating_ip(server, floating_ip)
if server:
self._delete_server(server)
self._delete_server(server)
LOG.debug("Output streams from in-instance script execution: "
"stdout: %(stdout)s, stderr: $(stderr)s" % dict(
stdout=out, stderr=err))
return {"data": out, "errors": err} return {"data": out, "errors": err}
def _release_server_floating_ip(self, server, floating_ip):
"""Release a floating ip associated to a server.
This method check that the given floating ip is associated with the
specified server and tries to dissociate it.
Once dissociated, release the floating ip to reintegrate
it to the pool of available ips.
:param server: The server to dissociate the floating ip from
:param floating_ip: The floating ip to release
"""
if floating_ip and server:
if self.check_ip_address(floating_ip)(server):
self._dissociate_floating_ip(server, floating_ip)
if floating_ip:
self._delete_floating_ip(floating_ip)

View File

@ -227,6 +227,46 @@ def image_valid_on_flavor(flavor_name, image_name):
return image_valid_on_flavor_validator return image_valid_on_flavor_validator
def network_exists(network_name):
def network_exists_validator(**kwargs):
network = kwargs.get(network_name, "private")
networks = [net.label for net in
kwargs["clients"].nova().networks.list()]
if network not in networks:
message = _("Network with name %(network)s not found. "
"Available networks: %(networks)s") % {
"network": network,
"networks": networks
}
return ValidationResult(False, message)
return ValidationResult()
return network_exists_validator
def external_network_exists(network_name, use_external_network):
def external_network_exists_validator(**kwargs):
if not kwargs.get(use_external_network, True):
return ValidationResult()
ext_network = kwargs.get(network_name, "public")
networks = [net.name for net in
kwargs["clients"].nova().floating_ip_pools.list()]
if ext_network not in networks:
message = _("External (floating) network with name %(network)s "
"not found. "
"Available networks: %(networks)s") % {
"network": ext_network,
"networks": networks
}
return ValidationResult(False, message)
return ValidationResult()
return external_network_exists_validator
def tempest_tests_exists(): def tempest_tests_exists():
"""Returns validator for tempest test.""" """Returns validator for tempest test."""
def tempest_test_exists_validator(**kwargs): def tempest_test_exists_validator(**kwargs):

View File

@ -186,6 +186,10 @@ class SSHError(RallyException):
msg_fmt = _("Remote command failed.") msg_fmt = _("Remote command failed.")
class ScriptError(RallyException):
msg_fmt = _("Script execution failed.")
class TaskInvalidStatus(RallyException): class TaskInvalidStatus(RallyException):
msg_fmt = _("Task `%(uuid)s` in `%(actual)s` status but `%(require)s` is " msg_fmt = _("Task `%(uuid)s` in `%(actual)s` status but `%(require)s` is "
"required.") "required.")

View File

@ -123,7 +123,7 @@ class SSH(object):
self._client.connect(self.host, username=self.user, self._client.connect(self.host, username=self.user,
port=self.port, pkey=self.pkey, port=self.port, pkey=self.pkey,
key_filename=self.key_filename, key_filename=self.key_filename,
password=self.password) password=self.password, timeout=1)
return self._client return self._client
except Exception as e: except Exception as e:
message = _("Exception %(exception_type)s was raised " message = _("Exception %(exception_type)s was raised "

View File

@ -35,6 +35,7 @@ class NovaScenarioTestCase(test.TestCase):
super(NovaScenarioTestCase, self).setUp() super(NovaScenarioTestCase, self).setUp()
self.server = mock.Mock() self.server = mock.Mock()
self.server1 = mock.Mock() self.server1 = mock.Mock()
self.floating_ip = mock.Mock()
self.image = mock.Mock() self.image = mock.Mock()
self.res_is = mockpatch.Patch(BM_UTILS + ".resource_is") self.res_is = mockpatch.Patch(BM_UTILS + ".resource_is")
self.get_fm = mockpatch.Patch(BM_UTILS + '.get_from_manager') self.get_fm = mockpatch.Patch(BM_UTILS + '.get_from_manager')
@ -270,3 +271,98 @@ class NovaScenarioTestCase(test.TestCase):
self.res_is.mock.assert_has_calls(mock.call('ACTIVE')) self.res_is.mock.assert_has_calls(mock.call('ACTIVE'))
self._test_atomic_action_timer(nova_scenario.atomic_actions(), self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.boot_servers') 'nova.boot_servers')
@mock.patch(NOVA_UTILS + '.NovaScenario.clients')
def test__list_floating_ip_pools(self, mock_clients):
pools_list = []
mock_clients("nova").floating_ip_pools.list.return_value = pools_list
nova_scenario = utils.NovaScenario()
return_pools_list = nova_scenario._list_floating_ip_pools()
self.assertEqual(pools_list, return_pools_list)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.list_floating_ip_pools')
@mock.patch(NOVA_UTILS + '.NovaScenario.clients')
def test__list_floating_ips(self, mock_clients):
floating_ips_list = []
mock_clients("nova").floating_ips.list.return_value = floating_ips_list
nova_scenario = utils.NovaScenario()
return_floating_ips_list = nova_scenario._list_floating_ips()
self.assertEqual(floating_ips_list, return_floating_ips_list)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.list_floating_ips')
@mock.patch(NOVA_UTILS + '.NovaScenario.clients')
def test__create_floating_ip(self, mock_clients):
mock_clients("nova").floating_ips.create.return_value = \
self.floating_ip
nova_scenario = utils.NovaScenario()
return_floating_ip = nova_scenario._create_floating_ip("public")
self.assertEqual(self.floating_ip, return_floating_ip)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.create_floating_ip')
@mock.patch(NOVA_UTILS + '.NovaScenario.clients')
def test__delete_floating_ip(self, mock_clients):
nova_scenario = utils.NovaScenario()
nova_scenario._delete_floating_ip(self.floating_ip)
mock_clients("nova").floating_ips.delete.assert_called_once_with(
self.floating_ip)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.delete_floating_ip')
def test__associate_floating_ip(self):
nova_scenario = utils.NovaScenario()
nova_scenario._associate_floating_ip(self.server, self.floating_ip)
self.server.add_floating_ip.assert_called_once_with(self.floating_ip,
fixed_address=None)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.associate_floating_ip')
def test__dissociate_floating_ip(self):
nova_scenario = utils.NovaScenario()
nova_scenario._dissociate_floating_ip(self.server, self.floating_ip)
self.server.remove_floating_ip.assert_called_once_with(
self.floating_ip)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.dissociate_floating_ip')
def test__check_ip_address(self):
nova_scenario = utils.NovaScenario()
fake_server = fakes.FakeServerManager().create("test_server",
"image_id_01",
"flavor_id_01")
fake_server.addresses = {
"private": [
{"version": 4, "addr": "1.2.3.4"},
]}
floating_ip = fakes.FakeFloatingIP()
floating_ip.ip = "10.20.30.40"
# Also test function check_ip_address accept a string as attr
self.assertFalse(
nova_scenario.check_ip_address(floating_ip.ip)(fake_server))
self.assertTrue(
nova_scenario.check_ip_address(floating_ip.ip, must_exist=False)
(fake_server))
fake_server.addresses["private"].append(
{"version": 4, "addr": floating_ip.ip}
)
# Also test function check_ip_address accept an object with attr ip
self.assertTrue(
nova_scenario.check_ip_address(floating_ip)
(fake_server))
self.assertFalse(
nova_scenario.check_ip_address(floating_ip, must_exist=False)
(fake_server))
@mock.patch(NOVA_UTILS + '.NovaScenario.clients')
def test__list_networks(self, mock_clients):
network_list = []
mock_clients("nova").networks.list.return_value = network_list
nova_scenario = utils.NovaScenario()
return_network_list = nova_scenario._list_networks()
self.assertEqual(network_list, return_network_list)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
'nova.list_networks')

View File

@ -14,7 +14,9 @@
# under the License. # under the License.
import mock import mock
import subprocess
from oslotest import mockpatch
from rally.benchmark.scenarios.vm import utils from rally.benchmark.scenarios.vm import utils
from tests import fakes from tests import fakes
from tests import test from tests import test
@ -25,6 +27,12 @@ VMTASKS_UTILS = "rally.benchmark.scenarios.vm.utils"
class VMScenarioTestCase(test.TestCase): class VMScenarioTestCase(test.TestCase):
def setUp(self):
super(VMScenarioTestCase, self).setUp()
self.wait_for = mockpatch.Patch(VMTASKS_UTILS +
".bench_utils.wait_for")
self.useFixture(self.wait_for)
@mock.patch('__builtin__.open') @mock.patch('__builtin__.open')
def test_run_action(self, mock_open): def test_run_action(self, mock_open):
mock_ssh = mock.MagicMock() mock_ssh = mock.MagicMock()
@ -35,32 +43,64 @@ class VMScenarioTestCase(test.TestCase):
mock_ssh.execute.assert_called_once_with('interpreter', mock_ssh.execute.assert_called_once_with('interpreter',
stdin=mock_file_handle) stdin=mock_file_handle)
def test_wait_for_network(self): def test_wait_for_ssh(self):
ssh = mock.MagicMock() ssh = mock.MagicMock()
vm_scenario = utils.VMScenario() vm_scenario = utils.VMScenario()
vm_scenario.wait_for_network(ssh) vm_scenario.wait_for_ssh(ssh)
ssh.wait.assert_called_once_with() ssh.wait.assert_called_once_with()
@mock.patch(VMTASKS_UTILS + ".VMScenario.ping_ip_address")
def test_wait_for_ping(self, mock_ping):
mock_ping.return_value = True
vm_scenario = utils.VMScenario()
vm_scenario.wait_for_ping("1.2.3.4")
self.wait_for.mock.assert_called_once_with("1.2.3.4",
is_ready=mock_ping,
timeout=120)
@mock.patch(VMTASKS_UTILS + ".VMScenario.run_action") @mock.patch(VMTASKS_UTILS + ".VMScenario.run_action")
@mock.patch(VMTASKS_UTILS + ".VMScenario.wait_for_ping")
@mock.patch("rally.sshutils.SSH") @mock.patch("rally.sshutils.SSH")
def test_run_command(self, mock_ssh_class, def test_run_command(self, mock_ssh_class, mock_wait_ping,
mock_run_action): mock_run_action):
mock_ssh_instance = mock.MagicMock() mock_ssh_instance = mock.MagicMock()
mock_ssh_class.return_value = mock_ssh_instance mock_ssh_class.return_value = mock_ssh_instance
fake_server = fakes.FakeServer()
fake_server.addresses = dict(
private=[dict(
version=4,
addr="1.2.3.4"
)]
)
vm_scenario = utils.VMScenario() vm_scenario = utils.VMScenario()
vm_scenario._context = {"user": {"keypair": {"private": "ssh"}}} vm_scenario._context = {"user": {"keypair": {"private": "ssh"}}}
vm_scenario.run_command(fake_server, "username", "private", 22, 4, vm_scenario.run_command("1.2.3.4", 22, "username", "int", "script")
"int", "script")
mock_ssh_class.assert_called_once_with("username", "1.2.3.4", mock_wait_ping.assert_called_once_with("1.2.3.4")
port=22, pkey="ssh") mock_ssh_class.assert_called_once_with("username", "1.2.3.4", port=22,
pkey="ssh")
mock_ssh_instance.wait.assert_called_once_with() mock_ssh_instance.wait.assert_called_once_with()
mock_run_action.assert_called_once_with(mock_ssh_instance, mock_run_action.assert_called_once_with(mock_ssh_instance,
"int", "script") "int", "script")
def test_check_network(self):
vm_scenario = utils.VMScenario()
fake_server = fakes.FakeServer()
fake_server.addresses = {}
self.assertRaises(ValueError,
vm_scenario.check_network, fake_server, "private")
fake_server.addresses["private_1"] = {
"version": 4,
"addr": "1.2.3.4"
}
vm_scenario.check_network(fake_server, "private_1")
@mock.patch("subprocess.Popen")
def test_ping_ip_address(self, mock_subprocess):
ping_process = mock.MagicMock()
ping_process.returncode = 0
mock_subprocess.return_value = ping_process
vm_scenario = utils.VMScenario()
host_ip = "1.2.3.4"
self.assertTrue(vm_scenario.ping_ip_address(host_ip))
mock_subprocess.assert_called_once_with(
['ping', '-c1', '-w1', host_ip],
stderr=subprocess.PIPE, stdout=subprocess.PIPE)
ping_process.wait.assert_called_once_with()

View File

@ -16,6 +16,7 @@
import mock import mock
from rally.benchmark.scenarios.vm import vmtasks from rally.benchmark.scenarios.vm import vmtasks
from tests import fakes
from tests import test from tests import test
@ -25,27 +26,119 @@ class VMTasksTestCase(test.TestCase):
def test_boot_runcommand_delete(self, mock_json_loads): def test_boot_runcommand_delete(self, mock_json_loads):
# Setup mocks # Setup mocks
scenario = vmtasks.VMTasks() scenario = vmtasks.VMTasks()
scenario._boot_server = mock.MagicMock(return_value="fake_server") fake_server = fakes.FakeServer()
fake_server.addresses = dict(
private=[dict(
version=4,
addr="1.2.3.4"
)]
)
scenario._boot_server = mock.MagicMock(return_value=fake_server)
scenario._generate_random_name = mock.MagicMock(return_value="name") scenario._generate_random_name = mock.MagicMock(return_value="name")
fake_floating_ip = fakes.FakeFloatingIP()
fake_floating_ip.ip = "4.3.2.1"
scenario._create_floating_ip = mock.MagicMock(
return_value=fake_floating_ip)
scenario._associate_floating_ip = mock.MagicMock()
#scenario._dissociate_floating_ip = mock.MagicMock()
#scenario._delete_floating_ip = mock.MagicMock()
scenario._release_server_floating_ip = mock.MagicMock()
#scenario.check_ip_address = mock.MagicMock(
# return_value=mock.MagicMock(return_value=True))
fake_floating_ip_pool = fakes.FakeFloatingIPPool()
fake_floating_ip_pool.name = "public"
scenario._list_floating_ip_pools = mock.MagicMock(
return_value=[fake_floating_ip_pool])
scenario.run_command = mock.MagicMock() scenario.run_command = mock.MagicMock()
scenario.run_command.return_value = ('code', 'stdout', 'stderr') scenario.run_command.return_value = (0, 'stdout', 'stderr')
scenario._delete_server = mock.MagicMock() scenario._delete_server = mock.MagicMock()
# Run scenario # Run scenario
scenario.boot_runcommand_delete( scenario.boot_runcommand_delete(
"image_id", "flavour_id", "script_path", "interpreter", "image_id", "flavour_id", "script_path", "interpreter",
network="network", username="username", ip_version="ip_version", fixed_network='private', floating_network='public',
port="port", fakearg="f") username="username", ip_version=4,
port=22, use_floatingip=True, fakearg="f")
# Assertions # Assertions
scenario._boot_server.assert_called_once_with( scenario._boot_server.assert_called_once_with(
'name', 'image_id', "flavour_id", key_name="rally_ssh_key", 'name', 'image_id', "flavour_id", key_name="rally_ssh_key",
fakearg="f") fakearg="f")
scenario._create_floating_ip.assert_called_once_with(
fake_floating_ip_pool.name)
scenario._associate_floating_ip.assert_called_once_with(
fake_server, fake_floating_ip)
scenario.run_command.assert_called_once_with( scenario.run_command.assert_called_once_with(
"fake_server", 'username', "network", "port", "ip_version", fake_floating_ip.ip, 22, 'username',
"interpreter", "script_path") "interpreter", "script_path")
mock_json_loads.assert_called_once_with('stdout') mock_json_loads.assert_called_once_with('stdout')
scenario._delete_server.assert_called_once_with("fake_server") #scenario._dissociate_floating_ip.assert_called_once_with(
# fake_server, fake_floating_ip)
#scenario._delete_floating_ip.assert_called_once_with(fake_floating_ip)
scenario._release_server_floating_ip.assert_called_once_with(
fake_server, fake_floating_ip)
scenario._delete_server.assert_called_once_with(fake_server)
@mock.patch("json.loads")
def test_boot_runcommand_delete_no_floating_ip(self, mock_json_loads):
# Setup mocks
scenario = vmtasks.VMTasks()
fake_server = fakes.FakeServer()
fake_server.addresses = dict(
private=[dict(
version=4,
addr="1.2.3.4"
)]
)
scenario._boot_server = mock.MagicMock(return_value=fake_server)
scenario._generate_random_name = mock.MagicMock(return_value="name")
scenario.run_command = mock.MagicMock()
scenario.run_command.return_value = (0, 'stdout', 'stderr')
scenario._delete_server = mock.MagicMock()
# Run scenario
scenario.boot_runcommand_delete(
"image_id", "flavour_id", "script_path", "interpreter",
fixed_network='private', floating_network='public',
username="username", ip_version=4,
port=22, use_floatingip=False, fakearg="f")
# Assertions
scenario._boot_server.assert_called_once_with(
'name', 'image_id', "flavour_id", key_name="rally_ssh_key",
fakearg="f")
scenario.run_command.assert_called_once_with(
fake_server.addresses['private'][0]['addr'], 22, 'username',
"interpreter", "script_path")
mock_json_loads.assert_called_once_with('stdout')
scenario._delete_server.assert_called_once_with(fake_server)
def test__release_server_floating_ip(self):
scenario = vmtasks.VMTasks()
fake_server = fakes.FakeServer()
fake_floating_ip = fakes.FakeFloatingIP()
scenario._dissociate_floating_ip = mock.MagicMock()
scenario._delete_floating_ip = mock.MagicMock()
scenario.check_ip_address = mock.MagicMock(
return_value=mock.MagicMock(return_value=True))
scenario._release_server_floating_ip(fake_server, fake_floating_ip)
scenario._dissociate_floating_ip.assert_called_once_with(
fake_server, fake_floating_ip)
scenario._delete_floating_ip.assert_called_once_with(fake_floating_ip)

View File

@ -260,6 +260,118 @@ class ValidationUtilsTestCase(test.TestCase):
self.assertFalse(result.is_valid) self.assertFalse(result.is_valid)
self.assertEqual(result.msg, "Image with id 'test_image_id' not found") self.assertEqual(result.msg, "Image with id 'test_image_id' not found")
@mock.patch("rally.osclients.Clients")
def test_network_exists(self, mock_osclients):
fakenclient = fakes.FakeNovaClient()
fake_network = fakes.FakeNetwork()
fake_network.label = "private"
fake_network.id = "net_id_1234"
fakenclient.networks.list = mock.MagicMock(
return_value=[fake_network])
mock_osclients.nova.return_value = fakenclient
validator = validation.network_exists("fixed_network")
network_name = "private"
result = validator(clients=mock_osclients,
fixed_network=network_name)
fakenclient.networks.list.assert_called_once_with()
self.assertTrue(result.is_valid)
self.assertIsNone(result.msg)
@mock.patch("rally.osclients.Clients")
def test_network_exists_fail(self, mock_osclients):
fakenclient = fakes.FakeNovaClient()
fake_network = fakes.FakeNetwork()
fake_network.label = "private"
fake_network.id = "net_id_1234"
fakenclient.networks.list = mock.MagicMock(
return_value=[fake_network])
mock_osclients.nova.return_value = fakenclient
validator = validation.network_exists("fixed_network")
network_name = "foo"
result = validator(clients=mock_osclients,
fixed_network=network_name)
fakenclient.networks.list.assert_called_once_with()
self.assertFalse(result.is_valid)
self.assertEqual(result.msg,
"Network with name foo not found. "
"Available networks: ['private']")
@mock.patch("rally.osclients.Clients")
def test_external_network_exists(self, mock_osclients):
fakenclient = fakes.FakeNovaClient()
fake_pool = fakes.FakeFloatingIPPool()
fake_pool.name = "floating"
fakenclient.floating_ip_pools.list = mock.MagicMock(
return_value=[fake_pool])
mock_osclients.nova.return_value = fakenclient
validator = validation.external_network_exists("floating_network",
"use_floatingip")
network_name = "floating"
result = validator(clients=mock_osclients,
floating_network=network_name)
fakenclient.floating_ip_pools.list.assert_called_once_with()
self.assertTrue(result.is_valid)
self.assertIsNone(result.msg)
@mock.patch("rally.osclients.Clients")
def test_external_network_exists_ignored(self, mock_osclients):
fakenclient = fakes.FakeNovaClient()
fake_pool = fakes.FakeFloatingIPPool()
fake_pool.name = "floating"
fakenclient.floating_ip_pools.list = mock.MagicMock(
return_value=[fake_pool])
mock_osclients.nova.return_value = fakenclient
validator = validation.external_network_exists("floating_network",
"use_floatingip")
network_name = "not_used"
result = validator(clients=mock_osclients,
floating_network=network_name,
use_floatingip=False)
self.assertFalse(fakenclient.floating_ip_pools.list.called)
self.assertTrue(result.is_valid)
self.assertIsNone(result.msg)
@mock.patch("rally.osclients.Clients")
def test_external_network_exists_fail(self, mock_osclients):
fakenclient = fakes.FakeNovaClient()
fake_pool = fakes.FakeFloatingIPPool()
fake_pool.name = "floating"
fakenclient.floating_ip_pools.list = mock.MagicMock(
return_value=[fake_pool])
mock_osclients.nova.return_value = fakenclient
validator = validation.external_network_exists("floating_network",
"use_floatingip")
network_name = "foo"
result = validator(clients=mock_osclients,
floating_network=network_name)
fakenclient.floating_ip_pools.list.assert_called_once_with()
self.assertFalse(result.is_valid)
self.assertEqual(result.msg, "External (floating) network with name "
"foo not found. "
"Available networks: ['floating']")
@mock.patch("rally.osclients.Clients") @mock.patch("rally.osclients.Clients")
def test_image_valid_on_flavor_flavor_not_exist(self, mock_osclients): def test_image_valid_on_flavor_flavor_not_exist(self, mock_osclients):
fakegclient = fakes.FakeGlanceClient() fakegclient = fakes.FakeGlanceClient()

View File

@ -134,6 +134,10 @@ class FakeFloatingIP(FakeResource):
pass pass
class FakeFloatingIPPool(FakeResource):
pass
class FakeTenant(FakeResource): class FakeTenant(FakeResource):
def __init__(self, manager, name): def __init__(self, manager, name):
@ -337,6 +341,12 @@ class FakeFloatingIPsManager(FakeManager):
return FakeFloatingIP(self) return FakeFloatingIP(self)
class FakeFloatingIPPoolsManager(FakeManager):
def create(self):
return FakeFloatingIPPool(self)
class FakeTenantsManager(FakeManager): class FakeTenantsManager(FakeManager):
def create(self, name): def create(self, name):
@ -565,6 +575,7 @@ class FakeNovaClient(object):
else: else:
self.servers = FakeServerManager(self.images) self.servers = FakeServerManager(self.images)
self.floating_ips = FakeFloatingIPsManager() self.floating_ips = FakeFloatingIPsManager()
self.floating_ip_pools = FakeFloatingIPPoolsManager()
self.networks = FakeNetworkManager() self.networks = FakeNetworkManager()
self.flavors = FakeFlavorManager() self.flavors = FakeFlavorManager()
self.keypairs = FakeKeypairManager() self.keypairs = FakeKeypairManager()

View File

@ -104,7 +104,7 @@ class SSHTestCase(test.TestCase):
mock.call.set_missing_host_key_policy('autoadd'), mock.call.set_missing_host_key_policy('autoadd'),
mock.call.connect('example.net', username='admin', mock.call.connect('example.net', username='admin',
port=22, pkey='key', key_filename=None, port=22, pkey='key', key_filename=None,
password=None), password=None, timeout=1),
] ]
self.assertEqual(client_calls, client.mock_calls) self.assertEqual(client_calls, client.mock_calls)