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:
|
failure_rate:
|
||||||
max: 20
|
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:
|
NeutronSubnets.delete_subnets:
|
||||||
-
|
-
|
||||||
runner:
|
runner:
|
||||||
|
@ -28,5 +28,12 @@ OPTS = {"openstack": [
|
|||||||
default=False,
|
default=False,
|
||||||
help="Whether Neutron API is older then OpenStack Newton or "
|
help="Whether Neutron API is older then OpenStack Newton or "
|
||||||
"not. Based in this option, some external fields for "
|
"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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from rally.common import cfg
|
||||||
|
from rally.common import logging
|
||||||
from rally.task import validation
|
from rally.task import validation
|
||||||
|
|
||||||
from rally_openstack import consts
|
from rally_openstack import consts
|
||||||
@ -20,6 +22,9 @@ from rally_openstack import scenario
|
|||||||
from rally_openstack.scenarios.neutron import utils
|
from rally_openstack.scenarios.neutron import utils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
"""Scenarios for Neutron."""
|
"""Scenarios for Neutron."""
|
||||||
|
|
||||||
|
|
||||||
@ -510,6 +515,75 @@ class CreateAndDeletePorts(utils.NeutronScenario):
|
|||||||
self._delete_port(port)
|
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",
|
@validation.add("required_services",
|
||||||
services=[consts.Service.NEUTRON])
|
services=[consts.Service.NEUTRON])
|
||||||
@validation.add("required_platform", platform="openstack", users=True)
|
@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(
|
scenario._update_port.assert_has_calls(
|
||||||
[mock.call(p, port_update_args) for p in ports])
|
[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):
|
def test_create_and_show_ports_positive(self):
|
||||||
port_create_args = {"allocation_pools": []}
|
port_create_args = {"allocation_pools": []}
|
||||||
ports_per_network = 1
|
ports_per_network = 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user