diff --git a/rally-jobs/nova.yaml b/rally-jobs/nova.yaml index 4b396377b8..f9140c37f6 100755 --- a/rally-jobs/nova.yaml +++ b/rally-jobs/nova.yaml @@ -1370,6 +1370,34 @@ failure_rate: max: 0 + NovaServers.boot_server_and_attach_interface: + - + args: + flavor: + name: {{flavor_name}} + image: + name: {{image_name}} + network_create_args: {} + subnet_create_args: {} + subnet_cidr_start: "1.1.0.0/30" + boot_server_args: {} + runner: + type: "constant" + times: 5 + concurrency: 2 + context: + network: {} + users: + tenants: 2 + users_per_tenant: 2 + quotas: + neutron: + network: -1 + subnet: -1 + sla: + failure_rate: + max: 0 + NovaAggregates.create_aggregate_add_host_and_boot_server: - args: diff --git a/rally/plugins/openstack/scenarios/nova/servers.py b/rally/plugins/openstack/scenarios/nova/servers.py index 5325b1f763..65ab0960e8 100755 --- a/rally/plugins/openstack/scenarios/nova/servers.py +++ b/rally/plugins/openstack/scenarios/nova/servers.py @@ -20,6 +20,7 @@ from rally import consts from rally import exceptions as rally_exceptions from rally.plugins.openstack import scenario from rally.plugins.openstack.scenarios.cinder import utils as cinder_utils +from rally.plugins.openstack.scenarios.neutron import utils as neutron_utils from rally.plugins.openstack.scenarios.nova import utils from rally.plugins.openstack.wrappers import network as network_wrapper from rally.task import types @@ -837,6 +838,38 @@ class BootAndAssociateFloatingIp(utils.NovaScenario, self._associate_floating_ip(server, address["ip"]) +@types.convert(image={"type": "glance_image"}, + flavor={"type": "nova_flavor"}) +@validation.image_valid_on_flavor("flavor", "image") +@validation.required_services(consts.Service.NOVA, consts.Service.NEUTRON) +@validation.add("required_platform", platform="openstack", users=True) +@scenario.configure(context={"cleanup": ["nova", "neutron"]}, + name="NovaServers.boot_server_and_attach_interface") +class BootServerAndAttachInterface(utils.NovaScenario, + neutron_utils.NeutronScenario): + def run(self, image, flavor, network_create_args=None, + subnet_create_args=None, subnet_cidr_start=None, + boot_server_args=None): + """Create server and subnet, then attach the interface to it. + + This scenario measures the "nova interface-attach" command performance. + + :param image: image to be used to boot an instance + :param flavor: flavor to be used to boot an instance + :param network_create_args: dict, POST /v2.0/networks request + options. + :param subnet_create_args: dict, POST /v2.0/subnets request options + :param subnet_cidr_start: str, start value for subnets CIDR + :param boot_server_args: Optional additional arguments for + server creation + """ + network = self._get_or_create_network(network_create_args) + self._create_subnet(network, subnet_create_args, subnet_cidr_start) + + server = self._boot_server(image, flavor, **boot_server_args) + self._attach_interface(server, net_id=network["network"]["id"]) + + @types.convert(image={"type": "glance_image"}, flavor={"type": "nova_flavor"}) @validation.image_valid_on_flavor("flavor", "image") diff --git a/rally/plugins/openstack/scenarios/nova/utils.py b/rally/plugins/openstack/scenarios/nova/utils.py index d51ce442c1..5d14f12f34 100644 --- a/rally/plugins/openstack/scenarios/nova/utils.py +++ b/rally/plugins/openstack/scenarios/nova/utils.py @@ -1282,3 +1282,18 @@ class NovaScenario(scenario.OpenStackScenario): """ return self.admin_clients("nova").aggregates.set_metadata(aggregate, metadata) + + @atomic.action_timer("nova.attach_interface") + def _attach_interface(self, server, port_id=None, + net_id=None, fixed_ip=None): + """Attach a network_interface to an instance. + + :param server: The :class:`Server` (or its ID) to attach to. + :param port_id: The port to attach. + :param network_id: the Network to attach + :param fixed_ip: the Fix_ip to attach + :returns the server that has attach interface + """ + return self.clients("nova").servers.interface_attach(server, + port_id, net_id, + fixed_ip) diff --git a/samples/tasks/scenarios/nova/boot-server-and-attach-interface.json b/samples/tasks/scenarios/nova/boot-server-and-attach-interface.json new file mode 100644 index 0000000000..09a964f82d --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-and-attach-interface.json @@ -0,0 +1,43 @@ +{% set flavor_name = flavor_name or "m1.tiny" %} +{% set image_name = "^(cirros.*-disk|TestVM)$" %} +{ + "NovaServers.boot_server_and_attach_interface": [ + { + "args": { + "flavor": { + "name": "{{flavor_name}}" + }, + "image": { + "name": "{{image_name}}" + }, + "network_create_args": {}, + "subnet_create_args": {}, + "subnet_cidr_start": "1.1.0.0/30", + "boot_server_args": {} + }, + "runner": { + "type": "constant", + "times": 5, + "concurrency": 2 + }, + "context": { + "network": {}, + "users": { + "tenants": 2, + "users_per_tenant": 2 + }, + "quotas": { + "neutron": { + "network": -1, + "subnet": -1 + } + } + }, + "sla": { + "failure_rate": { + "max": 0 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/nova/boot-server-and-attach-interface.yaml b/samples/tasks/scenarios/nova/boot-server-and-attach-interface.yaml new file mode 100644 index 0000000000..e8d3ddd861 --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-and-attach-interface.yaml @@ -0,0 +1,30 @@ +{% set flavor_name = flavor_name or "m1.tiny" %} +{% set image_name = "^(cirros.*-disk|TestVM)$" %} +--- + NovaServers.boot_server_and_attach_interface: + - + args: + flavor: + name: "{{flavor_name}}" + image: + name: "{{image_name}}" + network_create_args: {} + subnet_create_args: {} + subnet_cidr_start: "1.1.0.0/30" + boot_server_args: {} + runner: + type: "constant" + times: 5 + concurrency: 2 + context: + network: {} + users: + tenants: 2 + users_per_tenant: 2 + quotas: + neutron: + network: -1 + subnet: -1 + sla: + failure_rate: + max: 0 diff --git a/tests/unit/plugins/openstack/scenarios/nova/test_servers.py b/tests/unit/plugins/openstack/scenarios/nova/test_servers.py index 98a9b886b6..e7f17d59aa 100644 --- a/tests/unit/plugins/openstack/scenarios/nova/test_servers.py +++ b/tests/unit/plugins/openstack/scenarios/nova/test_servers.py @@ -847,6 +847,37 @@ class NovaServersTestCase(test.ScenarioTestCase): scenario._update_server.assert_called_once_with( scenario._boot_server.return_value, "desp") + def test_boot_server_and_attach_interface(self): + network_create_args = {"router:external": True} + subnet_create_args = {"allocation_pools": []} + subnet_cidr_start = "10.1.0.0/16" + boot_server_args = {} + net = mock.MagicMock() + subnet = mock.MagicMock() + server = mock.MagicMock() + + scenario = servers.BootServerAndAttachInterface(self.context) + scenario._get_or_create_network = mock.Mock(return_value=net) + scenario._create_subnet = mock.Mock(return_value=subnet) + scenario._boot_server = mock.Mock(return_value=server) + scenario._attach_interface = mock.Mock() + + scenario.run("image", "flavor", + network_create_args=network_create_args, + subnet_create_args=subnet_create_args, + subnet_cidr_start=subnet_cidr_start, + boot_server_args=boot_server_args) + + scenario._get_or_create_network.assert_called_once_with( + network_create_args) + scenario._create_subnet.assert_called_once_with(net, + subnet_create_args, + subnet_cidr_start) + scenario._boot_server.assert_called_once_with("image", "flavor", + **boot_server_args) + scenario._attach_interface.assert_called_once_with( + server, net_id=net["network"]["id"]) + def test_boot_server_from_volume_snapshot(self): fake_volume = mock.MagicMock(id="volume_id") fake_snapshot = mock.MagicMock(id="snapshot_id") diff --git a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py index b07cacf5c0..7f0af0feae 100644 --- a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py @@ -1278,6 +1278,19 @@ class NovaScenarioTestCase(test.ScenarioTestCase): self._test_atomic_action_timer(nova_scenario.atomic_actions(), "nova.uptime_hypervisor") + def test__attach_interface(self): + fake_server = mock.Mock() + nova_scenario = utils.NovaScenario() + + result = nova_scenario._attach_interface(fake_server, net_id="id") + self.assertEqual( + self.clients("nova").servers.interface_attach.return_value, + result) + self.clients("nova").servers.interface_attach.assert_called_once_with( + fake_server, None, "id", None) + self._test_atomic_action_timer(nova_scenario.atomic_actions(), + "nova.attach_interface") + def test_aggregate_set_metadata(self): nova_scenario = utils.NovaScenario(context=self.context) fake_metadata = {"test_metadata": "true"}