Skip to content

Commit

Permalink
Improvements for scenario VMTasks.boot_runcommand_delete
Browse files Browse the repository at this point in the history
Allow to run this scenario for neutron-based cluster, add support
of network context, and generally simplify its usage by making networking
configuration as simple as possible.
So user gets rid of confusing configration and focus on main job -
running arbitrary script on VM.

Changes:
  * neutron support (this scenario run only for nova-network before)
  * usage of rally.benchmark.wrappers.network, so networking implementation
    details are behind the scenes now and scenario code is simplier
  * scenario args `fixed_network' and `use_floating_ip' are removed
    because they make sense only for devstack where fixed network
    is available, so that is absolutely unusable on any real OpenStack
    and even confusing, so scenario will always use floating ip
  * default `start_cidr' values are set to C-class network
  * improvements in rally.benchmark.wrappers.network

Change-Id: I3ce9875dee3e62713afcab14cb8af35c27a2d083
  • Loading branch information
Alexander Maretskiy committed Jan 19, 2015
1 parent 74b457b commit 1f7fab6
Show file tree
Hide file tree
Showing 16 changed files with 565 additions and 435 deletions.
22 changes: 21 additions & 1 deletion rally-jobs/rally-neutron.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@
tenants: 3
users_per_tenant: 2
network:
start_cidr: "100.1.0.0/26"
start_cidr: "10.2.0.0/24"
networks_per_tenant: 2
sla:
failure_rate:
Expand Down Expand Up @@ -573,3 +573,23 @@
sla:
failure_rate:
max: 0

VMTasks.boot_runcommand_delete:
-
args:
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
script: "/home/jenkins/.rally/extra/instance_dd_test.sh"
interpreter: "/bin/sh"
username: "cirros"
runner:
type: "constant"
times: 4
concurrency: 2
context:
users:
tenants: 3
users_per_tenant: 2
network: {}
6 changes: 2 additions & 4 deletions rally-jobs/rally.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@
tenants: 3
users_per_tenant: 2
network:
start_cidr: "100.100.0.0/24"
start_cidr: "10.2.0.0/24"
networks_per_tenant: 2

-
Expand Down Expand Up @@ -995,9 +995,7 @@
name: "m1.tiny"
image:
name: {{image_name}}
fixed_network: "private"
floating_network: "public"
use_floatingip: true
script: "/home/jenkins/.rally/extra/instance_dd_test.sh"
interpreter: "/bin/sh"
username: "cirros"
Expand Down Expand Up @@ -1133,7 +1131,7 @@
tenants: 3
users_per_tenant: 2
network:
start_cidr: "100.1.0.0/26"
start_cidr: "10.2.0.0/24"
quotas:
nova:
security_groups: -1
Expand Down
1 change: 0 additions & 1 deletion rally/benchmark/context/cleanup/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def list(self):
class NeutronPort(NeutronMixin, base.ResourceManager):

def delete(self):

if self.raw_resource["device_owner"] == "network:router_interface":
self._manager().remove_interface_router(
self.raw_resource["device_id"],
Expand Down
2 changes: 1 addition & 1 deletion rally/benchmark/context/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Network(base.Context):
},
"additionalProperties": False
}
START_CIDR_DFLT = "100.1.0.0/26"
START_CIDR_DFLT = "10.2.0.0/24"

def __init__(self, context):
super(Network, self).__init__(context)
Expand Down
2 changes: 1 addition & 1 deletion rally/benchmark/scenarios/neutron/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def _create_subnet(self, network, subnet_create_args, start_cidr=None):
network_id = network["network"]["id"]

if not subnet_create_args.get("cidr"):
start_cidr = start_cidr or "1.0.0.0/24"
start_cidr = start_cidr or "10.2.0.0/24"
subnet_create_args["cidr"] = (
network_wrapper.generate_cidr(start_cidr=start_cidr))

Expand Down
7 changes: 3 additions & 4 deletions rally/benchmark/scenarios/nova/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,15 @@ def _boot_server(self, server_name, image_id, flavor_id,

if auto_assign_nic and not kwargs.get("nics", False):
nets = [net["id"]
for net in filter(lambda net: not net["external"],
self.context["tenant"]["networks"])]
for net in self.context["tenant"].get("networks", [])]
if nets:
# NOTE(amaretskiy): Balance servers among networks:
# divmod(iteration % tenants_num, nets_num)[1]
net_id = divmod(
net_idx = divmod(
(self.context["iteration"]
% self.context["config"]["users"]["tenants"]),
len(nets))[1]
kwargs["nics"] = [{"net-id": nets[net_id]}]
kwargs["nics"] = [{"net-id": nets[net_idx]}]

server = self.clients("nova").servers.create(
server_name, image_id, flavor_id, **kwargs)
Expand Down
20 changes: 0 additions & 20 deletions rally/benchmark/scenarios/vm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,26 +68,6 @@ def run_command(self, server_ip, port, username, password,
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):
ip = netaddr.IPAddress(host)
Expand Down
134 changes: 56 additions & 78 deletions rally/benchmark/scenarios/vm/vmtasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@

import json

from rally.benchmark.context import keypair
from rally.benchmark.scenarios import base
from rally.benchmark.scenarios.cinder import utils as cinder_utils
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.benchmark.wrappers import network as network_wrapper
from rally import consts
from rally import exceptions

Expand All @@ -38,7 +40,7 @@ def __init__(self, *args, **kwargs):
@validation.file_exists("script")
@validation.number("port", minval=1, maxval=65535, nullable=True,
integer_only=True)
@validation.external_network_exists("floating_network", "use_floatingip")
@validation.external_network_exists("floating_network")
@validation.required_services(consts.Service.NOVA, consts.Service.CINDER)
@validation.required_openstack(users=True)
@base.scenario(context={"cleanup": ["nova", "cinder"],
Expand All @@ -47,10 +49,8 @@ def boot_runcommand_delete(self, image, flavor,
script, interpreter, username,
password=None,
volume_args=None,
fixed_network="private",
floating_network="public",
ip_version=4, port=22,
use_floatingip=True,
floating_network=None,
port=22,
force_delete=False,
**kwargs):
"""Boot a server, run a script that outputs JSON, delete the server.
Expand All @@ -59,93 +59,71 @@ def boot_runcommand_delete(self, image, flavor,
:param image: glance image name to use for the vm
:param flavor: VM flavor name
:param script: script to run on the server, must output JSON mapping
:param script: script to run on server, must output JSON mapping
metric names to values (see the sample script below)
:param interpreter: The shell interpreter to use when running script
:param username: User to SSH to instance as
:param interpreter: server's interpreter to run the script
:param username: ssh username on server, str
:param password: Password on SSH authentication
:param volume_args: volume args when boot VM from volume
: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
:param force_delete: Whether to use force_delete for instances
: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.
:param volume_args: volume args for booting server from volume
:param floating_network: external network name, for floating ip
:param port: ssh port for SSH connection
:param force_delete: whether to use force_delete for servers
:param **kwargs: extra arguments for booting the server
:returns: dictionary with keys `data' and `errors':
data: dict, JSON output from the script
errors: str, raw data from the script's stderr stream
"""
if volume_args:
volume = self._create_volume(volume_args['size'], imageRef=None)
kwargs['block_device_mapping'] = {'vda': '%s:::1' % volume.id}

server = None
floating_ip = None
if volume_args:
volume = self._create_volume(volume_args["size"], imageRef=None)
kwargs["block_device_mapping"] = {"vda": "%s:::1" % volume.id}

fip = server = None
net_wrap = network_wrapper.wrap(self.clients)
kwargs.update({"auto_assign_nic": True,
"key_name": keypair.Keypair.KEYPAIR_NAME})
server = self._boot_server(
self._generate_random_name("rally_novaserver_"),
image, flavor, **kwargs)

if not server.networks:
raise RuntimeError(
"Server `%(server)s' is not connected to any network. "
"Use network context for auto-assigning networks "
"or provide `nics' argument with specific net-id." % {
"server": server.name})

internal_network = server.networks.keys()[0]
fixed_ip = server.addresses[internal_network][0]["addr"]
try:
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"]
fip = net_wrap.create_floating_ip(ext_network=floating_network,
int_network=internal_network,
tenant_id=server.tenant_id,
fixed_ip=fixed_ip)

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, password,
interpreter, script)
self._associate_floating_ip(server, fip["ip"],
fixed_address=fixed_ip)

code, out, err = self.run_command(fip["ip"], port, username,
password, interpreter, script)
if code:
raise exceptions.ScriptError(
"Error running script %(script)s."
"Error %(code)s: %(error)s" % {
"script": script,
"code": code,
"error": err
})

"script": script, "code": code, "error": err})
try:
out = json.loads(out)
data = 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
"Script %(script)s has not output valid JSON: "
"%(error)s" % {"script": script, "error": str(e)})

return {"data": data, "errors": err}

finally:
if use_floatingip:
self._release_server_floating_ip(server, floating_ip)
if server:
if fip:
if self.check_ip_address(fip["ip"])(server):
self._dissociate_floating_ip(server, fip["ip"])
net_wrap.delete_floating_ip(fip["id"], wait=True)
self._delete_server(server, force=force_delete)

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)
17 changes: 6 additions & 11 deletions rally/benchmark/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,30 +276,25 @@ def network_exists(config, clients, deployment, network_name):


@validator
def external_network_exists(config, clients, deployment, network_name,
use_external_network):
"""Validator checks that externatl network with network_name exist."""

if not config.get("args", {}).get(use_external_network, True):
def external_network_exists(config, clients, deployment, network_name):
"""Validator checks that external network with given name exists."""
ext_network = config.get("args", {}).get(network_name)
if not ext_network:
return ValidationResult()

ext_network = config.get("args", {}).get(network_name, "public")
networks = [net.name for net in clients.nova().floating_ip_pools.list()]

if isinstance(networks[0], dict):
if networks and isinstance(networks[0], dict):
networks = [n["name"] for n in networks]

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
}
"networks": networks}
return ValidationResult(False, message)

return ValidationResult()


@validator
def tempest_tests_exists(config, clients, deployment):
Expand Down

0 comments on commit 1f7fab6

Please sign in to comment.