Add Nova scenario to boot multiple servers in one request
The performance profile of, e.g., creating 10 VMs in a single request is often very, very different from creating 10 VMs in 10 requests, and the difference only grows with the number of VMs. This adds a scenario to boot an arbitrary number of servers with a single request, which can be compared to boot_and_delete_server, which boots them one server per request. Change-Id: I8cb5d6a4f27cce228b83d6a869ed75c505dd6626
This commit is contained in:
parent
b0b2521cbd
commit
76f743d130
@ -1102,6 +1102,26 @@
|
|||||||
failure_rate:
|
failure_rate:
|
||||||
max: 0
|
max: 0
|
||||||
|
|
||||||
|
NovaServers.boot_and_delete_multiple_servers:
|
||||||
|
-
|
||||||
|
args:
|
||||||
|
image:
|
||||||
|
name: {{image_name}}
|
||||||
|
flavor:
|
||||||
|
name: "m1.tiny"
|
||||||
|
count: 3
|
||||||
|
runner:
|
||||||
|
type: "constant"
|
||||||
|
times: 3
|
||||||
|
concurrency: 3
|
||||||
|
context:
|
||||||
|
users:
|
||||||
|
tenants: 1
|
||||||
|
users_per_tenant: 1
|
||||||
|
sla:
|
||||||
|
failure_rate:
|
||||||
|
max: 0
|
||||||
|
|
||||||
NovaServers.boot_and_list_server:
|
NovaServers.boot_and_list_server:
|
||||||
-
|
-
|
||||||
args:
|
args:
|
||||||
|
@ -103,6 +103,33 @@ class NovaServers(utils.NovaScenario,
|
|||||||
self.sleep_between(min_sleep, max_sleep)
|
self.sleep_between(min_sleep, max_sleep)
|
||||||
self._delete_server(server, force=force_delete)
|
self._delete_server(server, force=force_delete)
|
||||||
|
|
||||||
|
@types.set(image=types.ImageResourceType,
|
||||||
|
flavor=types.FlavorResourceType)
|
||||||
|
@validation.image_valid_on_flavor("flavor", "image")
|
||||||
|
@validation.required_services(consts.Service.NOVA)
|
||||||
|
@validation.required_openstack(admin=True, users=True)
|
||||||
|
@base.scenario(context={"cleanup": ["nova"]})
|
||||||
|
def boot_and_delete_multiple_servers(self, image, flavor, count=2,
|
||||||
|
min_sleep=0, max_sleep=0,
|
||||||
|
force_delete=False, **kwargs):
|
||||||
|
"""Boot multiple servers in a single request and delete them.
|
||||||
|
|
||||||
|
Deletion is done in parallel with one request per server, not
|
||||||
|
with a single request for all servers.
|
||||||
|
|
||||||
|
:param image: The image to boot from
|
||||||
|
:param flavor: Flavor used to boot instance
|
||||||
|
:param count: Number of instances to boot
|
||||||
|
:param min_sleep: Minimum sleep time in seconds (non-negative)
|
||||||
|
:param max_sleep: Maximum sleep time in seconds (non-negative)
|
||||||
|
:param force_delete: True if force_delete should be used
|
||||||
|
:param kwargs: Optional additional arguments for instance creation
|
||||||
|
"""
|
||||||
|
servers = self._boot_servers(image, flavor, 1, instances_amount=count,
|
||||||
|
**kwargs)
|
||||||
|
self.sleep_between(min_sleep, max_sleep)
|
||||||
|
self._delete_servers(servers, force=force_delete)
|
||||||
|
|
||||||
@types.set(image=types.ImageResourceType,
|
@types.set(image=types.ImageResourceType,
|
||||||
flavor=types.FlavorResourceType)
|
flavor=types.FlavorResourceType)
|
||||||
@validation.image_valid_on_flavor("flavor", "image")
|
@validation.image_valid_on_flavor("flavor", "image")
|
||||||
|
@ -393,6 +393,29 @@ class NovaScenario(base.Scenario):
|
|||||||
check_interval=CONF.benchmark.nova_server_delete_poll_interval
|
check_interval=CONF.benchmark.nova_server_delete_poll_interval
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _delete_servers(self, servers, force=False):
|
||||||
|
"""Delete multiple servers.
|
||||||
|
|
||||||
|
:param servers: A list of servers to delete
|
||||||
|
:param force: If True, force_delete will be used instead of delete.
|
||||||
|
"""
|
||||||
|
atomic_name = ("nova.%sdelete_servers") % (force and "force_" or "")
|
||||||
|
with base.AtomicAction(self, atomic_name):
|
||||||
|
for server in servers:
|
||||||
|
if force:
|
||||||
|
server.force_delete()
|
||||||
|
else:
|
||||||
|
server.delete()
|
||||||
|
|
||||||
|
for server in servers:
|
||||||
|
bench_utils.wait_for_delete(
|
||||||
|
server,
|
||||||
|
update_resource=bench_utils.get_from_manager(),
|
||||||
|
timeout=CONF.benchmark.nova_server_delete_timeout,
|
||||||
|
check_interval=CONF.
|
||||||
|
benchmark.nova_server_delete_poll_interval
|
||||||
|
)
|
||||||
|
|
||||||
@base.atomic_action_timer("nova.delete_image")
|
@base.atomic_action_timer("nova.delete_image")
|
||||||
def _delete_image(self, image):
|
def _delete_image(self, image):
|
||||||
"""Delete the given image.
|
"""Delete the given image.
|
||||||
|
26
samples/tasks/scenarios/nova/boot-and-delete-multiple.json
Normal file
26
samples/tasks/scenarios/nova/boot-and-delete-multiple.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"NovaServers.boot_and_delete_multiple_servers": [
|
||||||
|
{
|
||||||
|
"runner": {
|
||||||
|
"type": "constant",
|
||||||
|
"concurrency": 1,
|
||||||
|
"times": 1
|
||||||
|
},
|
||||||
|
"args": {
|
||||||
|
"count": 5,
|
||||||
|
"image": {
|
||||||
|
"name": "^cirros.*uec$"
|
||||||
|
},
|
||||||
|
"flavor": {
|
||||||
|
"name": "m1.tiny"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"users": {
|
||||||
|
"users_per_tenant": 1,
|
||||||
|
"tenants": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
17
samples/tasks/scenarios/nova/boot-and-delete-multiple.yaml
Normal file
17
samples/tasks/scenarios/nova/boot-and-delete-multiple.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
NovaServers.boot_and_delete_multiple_servers:
|
||||||
|
-
|
||||||
|
args:
|
||||||
|
image:
|
||||||
|
name: "^cirros.*uec$"
|
||||||
|
flavor:
|
||||||
|
name: "m1.tiny"
|
||||||
|
count: 5
|
||||||
|
runner:
|
||||||
|
type: "constant"
|
||||||
|
times: 1
|
||||||
|
concurrency: 1
|
||||||
|
context:
|
||||||
|
users:
|
||||||
|
tenants: 1
|
||||||
|
users_per_tenant: 1
|
@ -202,6 +202,27 @@ class NovaServersTestCase(test.TestCase):
|
|||||||
scenario._delete_server.assert_called_once_with(fake_server,
|
scenario._delete_server.assert_called_once_with(fake_server,
|
||||||
force=False)
|
force=False)
|
||||||
|
|
||||||
|
@mock.patch(NOVA_SERVERS_MODULE + ".NovaServers.clients")
|
||||||
|
def test_boot_and_delete_multiple_servers(self, mock_nova_clients):
|
||||||
|
mock_nova_clients.return_value = fakes.FakeNovaClient()
|
||||||
|
|
||||||
|
scenario = servers.NovaServers()
|
||||||
|
scenario._boot_servers = mock.Mock()
|
||||||
|
scenario._delete_servers = mock.Mock()
|
||||||
|
scenario.sleep_between = mock.Mock()
|
||||||
|
|
||||||
|
scenario.boot_and_delete_multiple_servers("img", "flavor", count=15,
|
||||||
|
min_sleep=10,
|
||||||
|
max_sleep=20,
|
||||||
|
fakearg="fakearg")
|
||||||
|
|
||||||
|
scenario._boot_servers.assert_called_once_with("img", "flavor", 1,
|
||||||
|
instances_amount=15,
|
||||||
|
fakearg="fakearg")
|
||||||
|
scenario.sleep_between.assert_called_once_with(10, 20)
|
||||||
|
scenario._delete_servers.assert_called_once_with(
|
||||||
|
scenario._boot_servers.return_value, force=False)
|
||||||
|
|
||||||
def test_boot_and_list_server(self):
|
def test_boot_and_list_server(self):
|
||||||
scenario = servers.NovaServers()
|
scenario = servers.NovaServers()
|
||||||
scenario._generate_random_name = mock.MagicMock(return_value="name")
|
scenario._generate_random_name = mock.MagicMock(return_value="name")
|
||||||
|
@ -416,6 +416,36 @@ class NovaScenarioTestCase(test.TestCase):
|
|||||||
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
|
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
|
||||||
"nova.unrescue_server")
|
"nova.unrescue_server")
|
||||||
|
|
||||||
|
@mock.patch(NOVA_UTILS + ".NovaScenario.clients")
|
||||||
|
def _test_delete_servers(self, mock_clients, force=False):
|
||||||
|
servers = [self.server, self.server1]
|
||||||
|
nova_scenario = utils.NovaScenario()
|
||||||
|
nova_scenario._delete_servers(servers, force=force)
|
||||||
|
check_interval = CONF.benchmark.nova_server_delete_poll_interval
|
||||||
|
expected = []
|
||||||
|
for server in servers:
|
||||||
|
expected.append(mock.call(
|
||||||
|
server, update_resource=self.gfm(),
|
||||||
|
check_interval=check_interval,
|
||||||
|
timeout=CONF.benchmark.nova_server_delete_timeout))
|
||||||
|
if force:
|
||||||
|
server.force_delete.assert_called_once_with()
|
||||||
|
self.assertFalse(server.delete.called)
|
||||||
|
else:
|
||||||
|
server.delete.assert_called_once_with()
|
||||||
|
self.assertFalse(server.force_delete.called)
|
||||||
|
|
||||||
|
self.assertEqual(expected, self.wait_for_delete.mock.mock_calls)
|
||||||
|
timer_name = "nova.%sdelete_servers" % ("force_" if force else "")
|
||||||
|
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
|
||||||
|
timer_name)
|
||||||
|
|
||||||
|
def test__default_delete_servers(self):
|
||||||
|
self._test_delete_servers()
|
||||||
|
|
||||||
|
def test__force_delete_servers(self):
|
||||||
|
self._test_delete_servers(force=True)
|
||||||
|
|
||||||
def test__delete_image(self):
|
def test__delete_image(self):
|
||||||
nova_scenario = utils.NovaScenario()
|
nova_scenario = utils.NovaScenario()
|
||||||
nova_scenario._delete_image(self.image)
|
nova_scenario._delete_image(self.image)
|
||||||
|
Loading…
Reference in New Issue
Block a user