diff --git a/.zuul.d/rally-task-neutron.yaml b/.zuul.d/rally-task-neutron.yaml index e8436a9a..39667934 100644 --- a/.zuul.d/rally-task-neutron.yaml +++ b/.zuul.d/rally-task-neutron.yaml @@ -3,3 +3,8 @@ parent: rally-task-at-devstack vars: rally_task: rally-jobs/neutron.yaml + devstack_plugins: + rally-openstack: https://git.openstack.org/openstack/rally-openstack + neutron: https://git.openstack.org/openstack/neutron + devstack_services: + neutron-trunk: true diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 911b55e0..e2d42348 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,14 @@ Changelog .. Release notes for existing releases are MUTABLE! If there is something that was missed or can be improved, feel free to change it! +unreleased +---------- + +Added +~~~~~ + +* Added neutron trunk scenarios + [1.3.0] - 2018-10-08 -------------------- diff --git a/rally-jobs/neutron.yaml b/rally-jobs/neutron.yaml index ee22f40e..5a4ddb7c 100644 --- a/rally-jobs/neutron.yaml +++ b/rally-jobs/neutron.yaml @@ -617,6 +617,28 @@ failure_rate: max: 20 + NeutronTrunks.create_and_list_trunks: + - + args: + network_create_args: {} + subport_count: 10 + runner: + type: "constant" + times: 100 + concurrency: 10 + context: + users: + tenants: 3 + users_per_tenant: 3 + quotas: + neutron: + network: -1 + port: -1 + trunk: -1 + sla: + failure_rate: + max: 10 + NeutronSubnets.delete_subnets: - runner: diff --git a/rally_openstack/contexts/quotas/neutron_quotas.py b/rally_openstack/contexts/quotas/neutron_quotas.py index f24d3a2b..ebb221b8 100644 --- a/rally_openstack/contexts/quotas/neutron_quotas.py +++ b/rally_openstack/contexts/quotas/neutron_quotas.py @@ -59,6 +59,10 @@ class NeutronQuotas(object): "health_monitor": { "type": "integer", "minimum": -1 + }, + "trunk": { + "type": "integer", + "minimum": -1 } } } diff --git a/rally_openstack/scenarios/neutron/network.py b/rally_openstack/scenarios/neutron/network.py index 7ec89b9b..3143b504 100644 --- a/rally_openstack/scenarios/neutron/network.py +++ b/rally_openstack/scenarios/neutron/network.py @@ -604,3 +604,34 @@ class DeleteSubnets(utils.NeutronScenario): # delete one of subnets based on the user sequential number subnet_id = network["subnets"][number] self._delete_subnet({"subnet": {"id": subnet_id}}) + + +@validation.add("number", param_name="subport_count", minval=1, + integer_only=True) +@validation.add("required_services", services=[consts.Service.NEUTRON]) +@validation.add("required_platform", platform="openstack", users=True) +@scenario.configure(context={"cleanup@openstack": ["neutron"]}, + name="NeutronTrunks.create_and_list_trunks") +class CreateAndListTrunks(utils.NeutronScenario): + + def run(self, network_create_args=None, subport_count=10): + """Create and a given number of trunks with subports and list all trunks + + :param network_create_args: dict, POST /v2.0/networks request + options. Deprecated. + :param trunk_count: int, number of trunk ports + :param subport_count: int, number of subports per trunk + """ + net = self._create_network(network_create_args or {}) + ports = [self._create_port(net, {}) for _ in range(subport_count)] + parent, subports = ports[0], ports[1:] + subport_payload = [{"port_id": p["port"]["id"], + "segmentation_type": "vlan", + "segmentation_id": seg_id} + for seg_id, p in enumerate(subports, start=1)] + trunk_payload = {"port_id": parent["port"]["id"], + "sub_ports": subport_payload} + self._create_trunk(trunk_payload) + self._update_port(parent, {"device_id": "sometrunk"}) + self._list_trunks() + self._list_ports_by_device_id("sometrunk") diff --git a/rally_openstack/scenarios/neutron/utils.py b/rally_openstack/scenarios/neutron/utils.py index 3fa27043..dde75f74 100644 --- a/rally_openstack/scenarios/neutron/utils.py +++ b/rally_openstack/scenarios/neutron/utils.py @@ -877,3 +877,19 @@ class NeutronScenario(scenario.OpenStackScenario): """ self.clients("neutron").delete_security_group_rule( security_group_rule) + + @atomic.action_timer("neutron.delete_trunk") + def _delete_trunk(self, trunk_port): + self.clients("neutron").delete_trunk(trunk_port["port_id"]) + + @atomic.action_timer("neutron.create_trunk") + def _create_trunk(self, trunk_payload): + return self.clients("neutron").create_trunk({"trunk": trunk_payload}) + + @atomic.optional_action_timer("neutron.list_trunks") + def _list_trunks(self, **kwargs): + return self.clients("neutron").list_trunks(**kwargs)["trunks"] + + @atomic.optional_action_timer("neutron.list_ports_by_device_id") + def _list_ports_by_device_id(self, device_id): + return self.clients("neutron").list_ports(device_id=device_id) diff --git a/samples/tasks/scenarios/neutron/create-and-list-trunks.json b/samples/tasks/scenarios/neutron/create-and-list-trunks.json new file mode 100644 index 00000000..0656a0be --- /dev/null +++ b/samples/tasks/scenarios/neutron/create-and-list-trunks.json @@ -0,0 +1,33 @@ +{ + "NeutronTrunks.create_and_list_trunks": [ + { + "args": { + "network_create_args": {}, + "subport_count": 10 + }, + "runner": { + "type": "constant", + "times": 100, + "concurrency": 10 + }, + "context": { + "users": { + "tenants": 3, + "users_per_tenant": 3 + }, + "quotas": { + "neutron": { + "network": -1, + "port": -1, + "trunk": -1 + } + } + }, + "sla": { + "failure_rate": { + "max": 0 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/neutron/create-and-list-trunks.yaml b/samples/tasks/scenarios/neutron/create-and-list-trunks.yaml new file mode 100644 index 00000000..ad90bc59 --- /dev/null +++ b/samples/tasks/scenarios/neutron/create-and-list-trunks.yaml @@ -0,0 +1,23 @@ + +--- + NeutronTrunks.create_and_list_trunks: + - + args: + network_create_args: {} + subport_count: 10 + runner: + type: "constant" + times: 100 + concurrency: 10 + context: + users: + tenants: 3 + users_per_tenant: 3 + quotas: + neutron: + network: -1 + port: -1 + trunk: -1 + sla: + failure_rate: + max: 0 diff --git a/tasks/openstack/scenario/neutron.yaml b/tasks/openstack/scenario/neutron.yaml index f5143c3b..005beb54 100644 --- a/tasks/openstack/scenario/neutron.yaml +++ b/tasks/openstack/scenario/neutron.yaml @@ -260,3 +260,21 @@ subnets_per_network: 15 dualstack: True router: {} + + NeutronTrunks.create_and_list_trunks: + - + args: + network_create_args: {} + subport_count: 10 + context: + {% call user_context(tenants_amount, users_amount, use_existing_users) %} + quotas: + neutron: + network: -1 + port: -1 + trunk: -1 + {% endcall %} + runner: + {{ constant_runner(concurrency=2*controllers_amount, times=8*controllers_amount, is_smoke=smoke) }} + sla: + {{ no_failures_sla() }} diff --git a/tests/unit/scenarios/neutron/test_network.py b/tests/unit/scenarios/neutron/test_network.py index 5affc7ce..7aea31c9 100644 --- a/tests/unit/scenarios/neutron/test_network.py +++ b/tests/unit/scenarios/neutron/test_network.py @@ -596,3 +596,24 @@ class NeutronNetworksTestCase(test.ScenarioTestCase): mock.call({"subnet": {"id": "subnet-5"}}) ], mock__delete_subnet.call_args_list) + + def test_create_and_list_trunks(self): + subport_count = 10 + network_create_args = {} + net = mock.MagicMock() + scenario = network.CreateAndListTrunks(self.context) + scenario._create_network = mock.Mock(return_value=net) + scenario._create_port = mock.MagicMock() + scenario._create_trunk = mock.MagicMock() + scenario._update_port = mock.Mock() + scenario._list_ports_by_device_id = mock.Mock() + scenario.run(network_create_args=network_create_args, + subport_count=subport_count) + scenario._create_network.assert_called_once_with( + network_create_args) + scenario._create_port.assert_has_calls( + [mock.call(net, {}) + for _ in range(subport_count)]) + self.assertEqual(1, scenario._create_trunk.call_count) + self.assertEqual(1, scenario._update_port.call_count) + self.assertEqual(1, scenario._list_ports_by_device_id.call_count) diff --git a/tests/unit/scenarios/neutron/test_utils.py b/tests/unit/scenarios/neutron/test_utils.py index ba7f5ccb..782c8d56 100644 --- a/tests/unit/scenarios/neutron/test_utils.py +++ b/tests/unit/scenarios/neutron/test_utils.py @@ -1292,6 +1292,47 @@ class NeutronScenarioTestCase(test.ScenarioTestCase): self._test_atomic_action_timer(self.scenario.atomic_actions(), "neutron.list_bgpvpn_router_assocs") + def test__delete_trunk(self): + trunk_port = {"trunk": {"port_id": "fake-id"}} + self.scenario._delete_trunk(trunk_port["trunk"]) + self.clients("neutron").delete_trunk.assert_called_once_with( + trunk_port["trunk"]["port_id"]) + self._test_atomic_action_timer(self.scenario.atomic_actions(), + "neutron.delete_trunk") + + def test__create_trunk(self): + port_id = "port-id" + subport_payload = [{"port_id": "subport-port-id", + "segmentation_type": "vlan", + "segmentation_id": 1}] + trunk_payload = { + "port_id": port_id, + "name": self.scenario.generate_random_name.return_value, + "sub_ports": subport_payload + } + expected_trunk_args = { + "trunk": trunk_payload + } + + self.scenario._create_trunk(trunk_payload) + self.clients("neutron").create_trunk.assert_called_once_with( + expected_trunk_args) + self._test_atomic_action_timer(self.scenario.atomic_actions(), + "neutron.create_trunk") + + def test__list_trunks(self): + trunks = [{"name": "trunk1"}, {"name": "trunk2"}] + self.clients("neutron").list_trunks.return_value = {"trunks": trunks} + self.assertEqual(trunks, self.scenario._list_trunks()) + self._test_atomic_action_timer(self.scenario.atomic_actions(), + "neutron.list_trunks") + + def test__list_ports_by_device_id(self): + device_id = "device-id" + self.scenario._list_ports_by_device_id(device_id) + self.clients("neutron").list_ports.assert_called_once_with( + device_id=device_id) + class NeutronScenarioFunctionalTestCase(test.FakeClientsScenarioTestCase):