New rally scenario: bind port
As discussed on the Denver 2019 PTG we should improve the performance testing of neutron port binding. This change introduces a scenario that does just that. The intent is to make this scenario quite realistic so it exercises all relevant code paths in neutron. Change-Id: I76356ca3fa2ea6ec6e1f2e34a4fce695b1509eb2 Partial-Bug: #1833674
This commit is contained in:
parent
8918b76bd2
commit
f2590c8cea
@ -642,6 +642,31 @@
|
||||
failure_rate:
|
||||
max: 20
|
||||
|
||||
NeutronNetworks.create_and_bind_ports:
|
||||
-
|
||||
args:
|
||||
ports_per_network: 5
|
||||
runner:
|
||||
type: "constant"
|
||||
times: {{smoke or 10}}
|
||||
concurrency: {{smoke or 5}}
|
||||
context:
|
||||
users:
|
||||
tenants: {{smoke or 2}}
|
||||
users_per_tenant: {{smoke or 1}}
|
||||
roles:
|
||||
- admin
|
||||
quotas:
|
||||
neutron:
|
||||
network: -1
|
||||
subnet: -1
|
||||
port: -1
|
||||
network: {}
|
||||
networking_agents: {}
|
||||
sla:
|
||||
failure_rate:
|
||||
max: 0
|
||||
|
||||
NeutronSubnets.delete_subnets:
|
||||
-
|
||||
runner:
|
||||
|
@ -28,5 +28,12 @@ OPTS = {"openstack": [
|
||||
default=False,
|
||||
help="Whether Neutron API is older then OpenStack Newton or "
|
||||
"not. Based in this option, some external fields for "
|
||||
"identifying resources can be applied.")
|
||||
"identifying resources can be applied."),
|
||||
cfg.ListOpt("neutron_bind_l2_agent_types",
|
||||
# default to agent types used in gate jobs
|
||||
default=[
|
||||
"Open vSwitch agent",
|
||||
"Linux bridge agent",
|
||||
],
|
||||
help="Neutron L2 agent types to find hosts to bind"),
|
||||
]}
|
||||
|
45
rally_openstack/contexts/network/networking_agents.py
Normal file
45
rally_openstack/contexts/network/networking_agents.py
Normal file
@ -0,0 +1,45 @@
|
||||
# Copyright 2019 Ericsson Software Technology
|
||||
#
|
||||
# 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.
|
||||
|
||||
from rally.common import logging
|
||||
from rally.common import validation
|
||||
from rally.task import context
|
||||
|
||||
from rally_openstack import consts
|
||||
from rally_openstack import osclients
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@validation.add("required_platform", platform="openstack", admin=True)
|
||||
@context.configure(name="networking_agents", platform="openstack", order=349)
|
||||
class NetworkingAgents(context.Context):
|
||||
"""This context supports querying Neutron agents in Rally."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
nc = osclients.Clients(self.context["admin"]["credential"]).neutron()
|
||||
agents = nc.list_agents()["agents"]
|
||||
# NOTE(bence romsics): If you ever add input parameters to this context
|
||||
# beware that here we use the same key in self.context as is used for
|
||||
# parameter passing, so we'll overwrite it.
|
||||
self.context["networking_agents"] = agents
|
||||
|
||||
def cleanup(self):
|
||||
"""Neutron agents were not created by Rally, so nothing to do."""
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from rally.common import cfg
|
||||
from rally.common import logging
|
||||
from rally.task import validation
|
||||
|
||||
from rally_openstack import consts
|
||||
@ -20,6 +22,9 @@ from rally_openstack import scenario
|
||||
from rally_openstack.scenarios.neutron import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
"""Scenarios for Neutron."""
|
||||
|
||||
|
||||
@ -510,6 +515,75 @@ class CreateAndDeletePorts(utils.NeutronScenario):
|
||||
self._delete_port(port)
|
||||
|
||||
|
||||
@validation.add("number", param_name="ports_per_network", minval=1,
|
||||
integer_only=True)
|
||||
@validation.add("required_services",
|
||||
services=[consts.Service.NEUTRON])
|
||||
@validation.add("required_contexts", contexts=["network", "networking_agents"])
|
||||
@validation.add("required_platform", platform="openstack",
|
||||
users=True, admin=True)
|
||||
@scenario.configure(context={"cleanup@openstack": ["neutron"],
|
||||
"networking_agents": {},
|
||||
"network": {}},
|
||||
name="NeutronNetworks.create_and_bind_ports",
|
||||
platform="openstack")
|
||||
class CreateAndBindPorts(utils.NeutronScenario):
|
||||
|
||||
def run(self, ports_per_network=1):
|
||||
"""Bind a given number of ports.
|
||||
|
||||
Measure the performance of port binding and all of its pre-requisites:
|
||||
* openstack network create
|
||||
* openstack subnet create --ip-version 4
|
||||
* openstack subnet create --ip-version 6
|
||||
* openstack port create
|
||||
* openstack port update (binding)
|
||||
|
||||
:param ports_per_network: int, number of ports for one network
|
||||
"""
|
||||
|
||||
# NOTE(bence romsics): Find a host where we can expect to bind
|
||||
# successfully. Look at agent types used in the gate.
|
||||
host_to_bind = None
|
||||
for agent in self.context["networking_agents"]:
|
||||
if (agent["admin_state_up"] and
|
||||
agent["alive"] and
|
||||
agent["agent_type"] in
|
||||
cfg.CONF.openstack.neutron_bind_l2_agent_types):
|
||||
host_to_bind = agent["host"]
|
||||
if host_to_bind is None:
|
||||
raise Exception(
|
||||
"No live agent of type(s) to bind was found: %s" %
|
||||
", ".join(cfg.CONF.openstack.neutron_bind_l2_agent_types))
|
||||
|
||||
tenant_id = self.context["tenant"]["id"]
|
||||
for network in self.context["tenants"][tenant_id]["networks"]:
|
||||
wrapped_network = {"network": network}
|
||||
|
||||
self._create_subnet(
|
||||
wrapped_network,
|
||||
start_cidr="10.2.0.0/24",
|
||||
subnet_create_args={},
|
||||
)
|
||||
self._create_subnet(
|
||||
wrapped_network,
|
||||
start_cidr="2001:db8:1:1::/64",
|
||||
subnet_create_args={},
|
||||
)
|
||||
|
||||
for i in range(ports_per_network):
|
||||
port = self._create_port(wrapped_network, port_create_args={})
|
||||
# port bind needs admin role
|
||||
self._update_port(
|
||||
port,
|
||||
port_update_args={
|
||||
"device_owner": "compute:nova",
|
||||
"device_id": "ba805478-85ff-11e9-a2e4-2b8dea218fc8",
|
||||
"binding:host_id": host_to_bind,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@validation.add("required_services",
|
||||
services=[consts.Service.NEUTRON])
|
||||
@validation.add("required_platform", platform="openstack", users=True)
|
||||
|
21
samples/tasks/contexts/networking-agents.json
Normal file
21
samples/tasks/contexts/networking-agents.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"Dummy.openstack": [
|
||||
{
|
||||
"args": {
|
||||
"sleep": 0.1
|
||||
},
|
||||
"runner": {
|
||||
"type": "constant",
|
||||
"times": 4,
|
||||
"concurrency": 2
|
||||
},
|
||||
"context": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 2
|
||||
},
|
||||
"networking_agents": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
14
samples/tasks/contexts/networking-agents.yaml
Normal file
14
samples/tasks/contexts/networking-agents.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
Dummy.openstack:
|
||||
-
|
||||
args:
|
||||
sleep: 0.1
|
||||
runner:
|
||||
type: "constant"
|
||||
times: 4
|
||||
concurrency: 2
|
||||
context:
|
||||
users:
|
||||
tenants: 1
|
||||
users_per_tenant: 2
|
||||
networking_agents: {}
|
35
samples/tasks/scenarios/neutron/create-and-bind-ports.json
Normal file
35
samples/tasks/scenarios/neutron/create-and-bind-ports.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"NeutronNetworks.create_and_bind_ports": [
|
||||
{
|
||||
"args": {
|
||||
"ports_per_network": 2
|
||||
},
|
||||
"runner": {
|
||||
"type": "constant",
|
||||
"times": 10,
|
||||
"concurrency": 5
|
||||
},
|
||||
"context": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 3
|
||||
},
|
||||
"roles": ["admin"],
|
||||
"quotas": {
|
||||
"neutron": {
|
||||
"network": -1,
|
||||
"subnet": -1,
|
||||
"port": -1
|
||||
}
|
||||
},
|
||||
"network": {},
|
||||
"networking_agents": {}
|
||||
},
|
||||
"sla": {
|
||||
"failure_rate": {
|
||||
"max": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
25
samples/tasks/scenarios/neutron/create-and-bind-ports.yaml
Normal file
25
samples/tasks/scenarios/neutron/create-and-bind-ports.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
NeutronNetworks.create_and_bind_ports:
|
||||
-
|
||||
args:
|
||||
ports_per_network: 2
|
||||
runner:
|
||||
type: "constant"
|
||||
times: 10
|
||||
concurrency: 5
|
||||
context:
|
||||
users:
|
||||
tenants: 2
|
||||
users_per_tenant: 3
|
||||
roles:
|
||||
- admin
|
||||
quotas:
|
||||
neutron:
|
||||
network: -1
|
||||
subnet: -1
|
||||
port: -1
|
||||
network: {}
|
||||
networking_agents: {}
|
||||
sla:
|
||||
failure_rate:
|
||||
max: 0
|
55
tests/unit/contexts/neutron/test_networking_agents.py
Normal file
55
tests/unit/contexts/neutron/test_networking_agents.py
Normal file
@ -0,0 +1,55 @@
|
||||
# Copyright 2019 Ericsson Software Technology
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
from rally_openstack.contexts.network import networking_agents
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally_openstack.contexts.network"
|
||||
|
||||
|
||||
class NetworkingAgentsTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NetworkingAgentsTestCase, self).setUp()
|
||||
|
||||
self.config = {}
|
||||
self.context = test.get_test_context()
|
||||
self.context.update({
|
||||
"users": [
|
||||
{"id": 1,
|
||||
"tenant_id": "tenant1",
|
||||
"credential": mock.Mock()},
|
||||
],
|
||||
"admin": {
|
||||
"credential": mock.Mock(),
|
||||
},
|
||||
"config": {
|
||||
"networking_agents": self.config,
|
||||
},
|
||||
})
|
||||
|
||||
@mock.patch("rally_openstack.osclients.Clients")
|
||||
def test_setup(self, mock_clients):
|
||||
context = networking_agents.NetworkingAgents(self.context)
|
||||
context.setup()
|
||||
mock_clients.assert_has_calls([
|
||||
mock.call().neutron().list_agents(),
|
||||
])
|
||||
|
||||
def test_cleanup(self):
|
||||
# NOTE(stpierre): Test that cleanup is not abstract
|
||||
networking_agents.NetworkingAgents(
|
||||
{"task": mock.MagicMock()}).cleanup()
|
@ -426,6 +426,45 @@ class NeutronNetworksTestCase(test.ScenarioTestCase):
|
||||
scenario._update_port.assert_has_calls(
|
||||
[mock.call(p, port_update_args) for p in ports])
|
||||
|
||||
def test_create_and_bind_ports(self):
|
||||
ports_per_network = 2
|
||||
ports = [mock.Mock() for _ in range(ports_per_network)]
|
||||
port_update_args = {
|
||||
"device_owner": "compute:nova",
|
||||
"device_id": "ba805478-85ff-11e9-a2e4-2b8dea218fc8",
|
||||
"binding:host_id": "fake-host",
|
||||
}
|
||||
|
||||
context = {
|
||||
"tenant": {"id": "fake-tenant-id"},
|
||||
"tenants": {
|
||||
"fake-tenant-id": {
|
||||
"networks": [
|
||||
mock.Mock()
|
||||
],
|
||||
},
|
||||
},
|
||||
"networking_agents": [{
|
||||
"host": "fake-host",
|
||||
"alive": True,
|
||||
"admin_state_up": True,
|
||||
"agent_type": "Open vSwitch agent",
|
||||
}],
|
||||
}
|
||||
scenario = network.CreateAndBindPorts(context)
|
||||
|
||||
scenario._create_network = mock.Mock()
|
||||
scenario._create_subnet = mock.Mock()
|
||||
scenario._create_port = mock.Mock(
|
||||
side_effect=ports)
|
||||
scenario._update_port = mock.Mock()
|
||||
|
||||
scenario.run(
|
||||
ports_per_network=ports_per_network)
|
||||
|
||||
scenario._update_port.assert_has_calls(
|
||||
[mock.call(p, port_update_args=port_update_args) for p in ports])
|
||||
|
||||
def test_create_and_show_ports_positive(self):
|
||||
port_create_args = {"allocation_pools": []}
|
||||
ports_per_network = 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user