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:
parent
044abcbd8f
commit
3d8b423db3
@ -8,6 +8,9 @@
|
||||
"image": {
|
||||
"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",
|
||||
"interpreter": "/bin/sh",
|
||||
"username": "cirros"
|
||||
|
@ -6,6 +6,9 @@
|
||||
name: "m1.nano"
|
||||
image:
|
||||
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"
|
||||
interpreter: "/bin/sh"
|
||||
username: "cirros"
|
||||
|
@ -102,6 +102,24 @@
|
||||
type: "constant"
|
||||
times: 100
|
||||
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:
|
||||
users:
|
||||
tenants: 1
|
||||
|
@ -321,3 +321,44 @@
|
||||
users:
|
||||
tenants: 3
|
||||
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
|
||||
|
@ -21,6 +21,7 @@ from rally.benchmark.scenarios import base
|
||||
from rally.benchmark.scenarios import utils as scenario_utils
|
||||
from rally.benchmark import utils as bench_utils
|
||||
|
||||
|
||||
nova_benchmark_opts = []
|
||||
option_names_and_defaults = [
|
||||
# action, prepoll delay, timeout, poll interval
|
||||
@ -93,6 +94,7 @@ class NovaScenario(base.Scenario):
|
||||
|
||||
server = self.clients("nova").servers.create(server_name, image_id,
|
||||
flavor_id, **kwargs)
|
||||
|
||||
time.sleep(CONF.benchmark.nova_server_boot_prepoll_delay)
|
||||
server = bench_utils.wait_for(
|
||||
server,
|
||||
@ -312,3 +314,86 @@ class NovaScenario(base.Scenario):
|
||||
check_interval=CONF.benchmark.nova_server_boot_poll_interval
|
||||
) for server in 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()
|
||||
|
@ -14,8 +14,11 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import subprocess
|
||||
|
||||
from rally.benchmark.scenarios import base
|
||||
from rally.benchmark.scenarios import utils as scenario_utils
|
||||
from rally.benchmark import utils as bench_utils
|
||||
from rally import sshutils
|
||||
|
||||
|
||||
@ -29,12 +32,19 @@ class VMScenario(base.Scenario):
|
||||
"""
|
||||
return ssh.execute(interpreter, stdin=open(script, "rb"))
|
||||
|
||||
@scenario_utils.atomic_action_timer('vm.wait_for_network')
|
||||
def wait_for_network(self, ssh):
|
||||
@scenario_utils.atomic_action_timer('vm.wait_for_ssh')
|
||||
def wait_for_ssh(self, ssh):
|
||||
ssh.wait()
|
||||
|
||||
def run_command(self, server, username, network, port, ip_version,
|
||||
interpreter, script):
|
||||
@scenario_utils.atomic_action_timer('vm.wait_for_ping')
|
||||
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.
|
||||
|
||||
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
|
||||
execute the command.
|
||||
"""
|
||||
|
||||
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"]
|
||||
self.wait_for_ping(server_ip)
|
||||
ssh = sshutils.SSH(username, server_ip, port=port,
|
||||
pkey=self.context()["user"]["keypair"]["private"])
|
||||
|
||||
self.wait_for_network(ssh)
|
||||
self.wait_for_ssh(ssh)
|
||||
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
|
||||
|
@ -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 import types as types
|
||||
from rally.benchmark import validation
|
||||
from rally.openstack.common.gettextutils import _ # noqa
|
||||
from rally.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
from rally import exceptions
|
||||
|
||||
|
||||
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.number("port", minval=1, maxval=65535,
|
||||
nullable=True, integer_only=True))
|
||||
@validation.add(validation.external_network_exists("floating_network",
|
||||
"use_floatingip"))
|
||||
@base.scenario(context={"cleanup": ["nova"],
|
||||
"keypair": {}, "allow_ssh": {}})
|
||||
def boot_runcommand_delete(self, image, flavor,
|
||||
script, interpreter, network='private',
|
||||
username='ubuntu', ip_version=4,
|
||||
port=22, **kwargs):
|
||||
script, interpreter, username,
|
||||
fixed_network="private",
|
||||
floating_network="public",
|
||||
ip_version=4, port=22,
|
||||
use_floatingip=True, **kwargs):
|
||||
"""Boot server, run a script that outputs JSON, delete server.
|
||||
|
||||
Parameters:
|
||||
script: script to run on the server, must output JSON mapping metric
|
||||
names to values. See sample script below.
|
||||
network: Network to choose address to connect to instance from
|
||||
username: User to SSH to instance as
|
||||
ip_version: Version of ip protocol to use for connection
|
||||
:param script: script to run on the server, must output JSON mapping
|
||||
metric names to values. See sample script below.
|
||||
:param interpreter: The shell interpreter to use when running script
|
||||
:param username: User to SSH to instance as
|
||||
:param fixed_network: Network where instance is part of
|
||||
: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
|
||||
script's standard error stream.
|
||||
|
||||
|
||||
Example Script in doc/samples/support/instance_dd_test.sh
|
||||
"""
|
||||
server = self._boot_server(
|
||||
self._generate_random_name("rally_novaserver_"),
|
||||
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)
|
||||
server = None
|
||||
floating_ip = None
|
||||
try:
|
||||
out = json.loads(out)
|
||||
except ValueError:
|
||||
LOG.warning(_("Script %s did not output valid JSON.") % script)
|
||||
server = self._boot_server(
|
||||
self._generate_random_name("rally_novaserver_"),
|
||||
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}
|
||||
|
||||
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)
|
||||
|
@ -227,6 +227,46 @@ def image_valid_on_flavor(flavor_name, image_name):
|
||||
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():
|
||||
"""Returns validator for tempest test."""
|
||||
def tempest_test_exists_validator(**kwargs):
|
||||
|
@ -186,6 +186,10 @@ class SSHError(RallyException):
|
||||
msg_fmt = _("Remote command failed.")
|
||||
|
||||
|
||||
class ScriptError(RallyException):
|
||||
msg_fmt = _("Script execution failed.")
|
||||
|
||||
|
||||
class TaskInvalidStatus(RallyException):
|
||||
msg_fmt = _("Task `%(uuid)s` in `%(actual)s` status but `%(require)s` is "
|
||||
"required.")
|
||||
|
@ -123,7 +123,7 @@ class SSH(object):
|
||||
self._client.connect(self.host, username=self.user,
|
||||
port=self.port, pkey=self.pkey,
|
||||
key_filename=self.key_filename,
|
||||
password=self.password)
|
||||
password=self.password, timeout=1)
|
||||
return self._client
|
||||
except Exception as e:
|
||||
message = _("Exception %(exception_type)s was raised "
|
||||
|
@ -35,6 +35,7 @@ class NovaScenarioTestCase(test.TestCase):
|
||||
super(NovaScenarioTestCase, self).setUp()
|
||||
self.server = mock.Mock()
|
||||
self.server1 = mock.Mock()
|
||||
self.floating_ip = mock.Mock()
|
||||
self.image = mock.Mock()
|
||||
self.res_is = mockpatch.Patch(BM_UTILS + ".resource_is")
|
||||
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._test_atomic_action_timer(nova_scenario.atomic_actions(),
|
||||
'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')
|
||||
|
@ -14,7 +14,9 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import subprocess
|
||||
|
||||
from oslotest import mockpatch
|
||||
from rally.benchmark.scenarios.vm import utils
|
||||
from tests import fakes
|
||||
from tests import test
|
||||
@ -25,6 +27,12 @@ VMTASKS_UTILS = "rally.benchmark.scenarios.vm.utils"
|
||||
|
||||
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')
|
||||
def test_run_action(self, mock_open):
|
||||
mock_ssh = mock.MagicMock()
|
||||
@ -35,32 +43,64 @@ class VMScenarioTestCase(test.TestCase):
|
||||
mock_ssh.execute.assert_called_once_with('interpreter',
|
||||
stdin=mock_file_handle)
|
||||
|
||||
def test_wait_for_network(self):
|
||||
def test_wait_for_ssh(self):
|
||||
ssh = mock.MagicMock()
|
||||
vm_scenario = utils.VMScenario()
|
||||
vm_scenario.wait_for_network(ssh)
|
||||
vm_scenario.wait_for_ssh(ssh)
|
||||
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.wait_for_ping")
|
||||
@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_ssh_instance = mock.MagicMock()
|
||||
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._context = {"user": {"keypair": {"private": "ssh"}}}
|
||||
vm_scenario.run_command(fake_server, "username", "private", 22, 4,
|
||||
"int", "script")
|
||||
vm_scenario.run_command("1.2.3.4", 22, "username", "int", "script")
|
||||
|
||||
mock_ssh_class.assert_called_once_with("username", "1.2.3.4",
|
||||
port=22, pkey="ssh")
|
||||
mock_wait_ping.assert_called_once_with("1.2.3.4")
|
||||
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_run_action.assert_called_once_with(mock_ssh_instance,
|
||||
"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()
|
||||
|
@ -16,6 +16,7 @@
|
||||
import mock
|
||||
|
||||
from rally.benchmark.scenarios.vm import vmtasks
|
||||
from tests import fakes
|
||||
from tests import test
|
||||
|
||||
|
||||
@ -25,27 +26,119 @@ class VMTasksTestCase(test.TestCase):
|
||||
def test_boot_runcommand_delete(self, mock_json_loads):
|
||||
# Setup mocks
|
||||
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")
|
||||
|
||||
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.return_value = ('code', 'stdout', 'stderr')
|
||||
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",
|
||||
network="network", username="username", ip_version="ip_version",
|
||||
port="port", fakearg="f")
|
||||
fixed_network='private', floating_network='public',
|
||||
username="username", ip_version=4,
|
||||
port=22, use_floatingip=True, fakearg="f")
|
||||
|
||||
# Assertions
|
||||
scenario._boot_server.assert_called_once_with(
|
||||
'name', 'image_id', "flavour_id", key_name="rally_ssh_key",
|
||||
fakearg="f")
|
||||
'name', 'image_id', "flavour_id", key_name="rally_ssh_key",
|
||||
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(
|
||||
"fake_server", 'username', "network", "port", "ip_version",
|
||||
fake_floating_ip.ip, 22, 'username',
|
||||
"interpreter", "script_path")
|
||||
|
||||
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)
|
||||
|
@ -260,6 +260,118 @@ class ValidationUtilsTestCase(test.TestCase):
|
||||
self.assertFalse(result.is_valid)
|
||||
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")
|
||||
def test_image_valid_on_flavor_flavor_not_exist(self, mock_osclients):
|
||||
fakegclient = fakes.FakeGlanceClient()
|
||||
|
@ -134,6 +134,10 @@ class FakeFloatingIP(FakeResource):
|
||||
pass
|
||||
|
||||
|
||||
class FakeFloatingIPPool(FakeResource):
|
||||
pass
|
||||
|
||||
|
||||
class FakeTenant(FakeResource):
|
||||
|
||||
def __init__(self, manager, name):
|
||||
@ -337,6 +341,12 @@ class FakeFloatingIPsManager(FakeManager):
|
||||
return FakeFloatingIP(self)
|
||||
|
||||
|
||||
class FakeFloatingIPPoolsManager(FakeManager):
|
||||
|
||||
def create(self):
|
||||
return FakeFloatingIPPool(self)
|
||||
|
||||
|
||||
class FakeTenantsManager(FakeManager):
|
||||
|
||||
def create(self, name):
|
||||
@ -565,6 +575,7 @@ class FakeNovaClient(object):
|
||||
else:
|
||||
self.servers = FakeServerManager(self.images)
|
||||
self.floating_ips = FakeFloatingIPsManager()
|
||||
self.floating_ip_pools = FakeFloatingIPPoolsManager()
|
||||
self.networks = FakeNetworkManager()
|
||||
self.flavors = FakeFlavorManager()
|
||||
self.keypairs = FakeKeypairManager()
|
||||
|
@ -104,7 +104,7 @@ class SSHTestCase(test.TestCase):
|
||||
mock.call.set_missing_host_key_policy('autoadd'),
|
||||
mock.call.connect('example.net', username='admin',
|
||||
port=22, pkey='key', key_filename=None,
|
||||
password=None),
|
||||
password=None, timeout=1),
|
||||
]
|
||||
self.assertEqual(client_calls, client.mock_calls)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user