Merge "Add context that creates servers using EC2 api"
This commit is contained in:
commit
8b2c7a3803
0
rally/plugins/openstack/context/ec2/__init__.py
Normal file
0
rally/plugins/openstack/context/ec2/__init__.py
Normal file
98
rally/plugins/openstack/context/ec2/servers.py
Normal file
98
rally/plugins/openstack/context/ec2/servers.py
Normal file
@ -0,0 +1,98 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.context.cleanup import manager as resource_manager
|
||||
from rally.plugins.openstack.scenarios.ec2 import utils as ec2_utils
|
||||
from rally.task import context
|
||||
from rally.task import types
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.configure(name="ec2_servers", order=460)
|
||||
class EC2ServerGenerator(context.Context):
|
||||
"""Context class for adding temporary servers for benchmarks.
|
||||
|
||||
Servers are added for each tenant.
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"flavor": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"servers_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
"required": ["image", "flavor", "servers_per_tenant"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `EC2 Servers`"))
|
||||
def setup(self):
|
||||
image = self.config["image"]
|
||||
flavor = self.config["flavor"]
|
||||
|
||||
clients = osclients.Clients(self.context["users"][0]["endpoint"])
|
||||
image_id = types.EC2ImageResourceType.transform(clients=clients,
|
||||
resource_config=image)
|
||||
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
LOG.debug("Booting servers for tenant %s "
|
||||
% (user["tenant_id"]))
|
||||
user_clients = osclients.Clients(user["endpoint"])
|
||||
ec2_scenario = ec2_utils.EC2Scenario(clients=user_clients)
|
||||
|
||||
LOG.debug(
|
||||
"Calling _boot_servers with "
|
||||
"image_id={image_id} flavor_name={flavor_name} "
|
||||
"servers_per_tenant={servers_per_tenant}".format(
|
||||
image_id=image_id, flavor_name=flavor["name"],
|
||||
servers_per_tenant=self.config["servers_per_tenant"]))
|
||||
|
||||
servers = ec2_scenario._boot_servers(
|
||||
image_id, flavor["name"], self.config["servers_per_tenant"])
|
||||
|
||||
current_servers = [server.id for server in servers]
|
||||
|
||||
self.context["tenants"][tenant_id]["ec2_servers"] = current_servers
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `EC2 Servers`"))
|
||||
def cleanup(self):
|
||||
resource_manager.cleanup(names=["ec2.servers"],
|
||||
users=self.context.get("users", []))
|
@ -41,4 +41,4 @@ class EC2Servers(utils.EC2Scenario):
|
||||
:param flavor: flavor to be used to boot an instance
|
||||
:param kwargs: optional additional arguments for server creation
|
||||
"""
|
||||
self._boot_server(image, flavor, **kwargs)
|
||||
self._boot_servers(image, flavor, **kwargs)
|
||||
|
@ -48,33 +48,38 @@ CONF.register_opts(EC2_BENCHMARK_OPTS, group=benchmark_group)
|
||||
class EC2Scenario(scenario.OpenStackScenario):
|
||||
"""Base class for EC2 scenarios with basic atomic actions."""
|
||||
|
||||
RESOURCE_NAME_PREFIX = "rally_ec2server_"
|
||||
RESOURCE_NAME_LENGTH = 16
|
||||
@base.atomic_action_timer("ec2.boot_servers")
|
||||
def _boot_servers(self, image_id, flavor_name,
|
||||
instance_num=1, **kwargs):
|
||||
"""Boot multiple servers.
|
||||
|
||||
@base.atomic_action_timer("ec2.boot_server")
|
||||
def _boot_server(self, image_id, flavor_name, **kwargs):
|
||||
"""Boot a server.
|
||||
|
||||
Returns when the server is actually booted and in "Running" state.
|
||||
Returns when all the servers are actually booted and are in the
|
||||
"Running" state.
|
||||
|
||||
:param image_id: ID of the image to be used for server creation
|
||||
:param flavor_name: Name of the flavor to be used for server creation
|
||||
:param kwargs: other optional parameters to initialize the server
|
||||
:returns: EC2 Server instance
|
||||
:param instance_num: Number of instances to boot
|
||||
:param kwargs: Other optional parameters to boot servers
|
||||
|
||||
:returns: List of created server objects
|
||||
"""
|
||||
reservation = self.clients("ec2").run_instances(
|
||||
image_id=image_id, instance_type=flavor_name, **kwargs)
|
||||
server = reservation.instances[0]
|
||||
image_id=image_id,
|
||||
instance_type=flavor_name,
|
||||
min_count=instance_num,
|
||||
max_count=instance_num,
|
||||
**kwargs)
|
||||
servers = [instance for instance in reservation.instances]
|
||||
|
||||
time.sleep(CONF.benchmark.ec2_server_boot_prepoll_delay)
|
||||
server = utils.wait_for(
|
||||
servers = [utils.wait_for(
|
||||
server,
|
||||
is_ready=utils.resource_is("RUNNING"),
|
||||
update_resource=self._update_resource,
|
||||
timeout=CONF.benchmark.ec2_server_boot_timeout,
|
||||
check_interval=CONF.benchmark.ec2_server_boot_poll_interval
|
||||
)
|
||||
return server
|
||||
) for server in servers]
|
||||
return servers
|
||||
|
||||
def _update_resource(self, resource):
|
||||
resource.update()
|
||||
|
110
tests/unit/plugins/openstack/context/ec2/test_servers.py
Normal file
110
tests/unit/plugins/openstack/context/ec2/test_servers.py
Normal file
@ -0,0 +1,110 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 copy
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.ec2 import servers
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.ec2"
|
||||
SCN = "rally.plugins.openstack.scenarios"
|
||||
TYP = "rally.task.types"
|
||||
|
||||
|
||||
class EC2ServerGeneratorTestCase(test.TestCase):
|
||||
|
||||
def _gen_tenants_and_users(self, tenants_count, users_per_tenant):
|
||||
tenants = {}
|
||||
for id in range(tenants_count):
|
||||
tenants[str(id)] = dict(name=str(id))
|
||||
|
||||
users = []
|
||||
for tenant_id in tenants.keys():
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": tenant_id,
|
||||
"endpoint": "endpoint"})
|
||||
return tenants, users
|
||||
|
||||
def _get_context(self, users, tenants):
|
||||
return {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 5,
|
||||
"concurrent": 10},
|
||||
"ec2_servers": {
|
||||
"servers_per_tenant": 5,
|
||||
"image": {"name": "foo_image"},
|
||||
"flavor": {"name": "foo_flavor"}
|
||||
}
|
||||
},
|
||||
"admin": {"endpoint": mock.MagicMock()},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
@mock.patch("%s.ec2.utils.EC2Scenario._boot_servers" % SCN,
|
||||
return_value=[fakes.FakeServer(id=str(i)) for i in range(5)])
|
||||
@mock.patch("%s.EC2ImageResourceType.transform" % TYP,
|
||||
return_value=mock.MagicMock())
|
||||
@mock.patch("%s.servers.osclients" % CTX, return_value=fakes.FakeClients())
|
||||
def test_setup(self, mock_osclients,
|
||||
mock_ec2_image_resource_type_transform,
|
||||
mock_ec2_scenario__boot_servers):
|
||||
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
servers_per_tenant = 5
|
||||
|
||||
tenants, users = self._gen_tenants_and_users(tenants_count,
|
||||
users_per_tenant)
|
||||
|
||||
real_context = self._get_context(users, tenants)
|
||||
|
||||
new_context = copy.deepcopy(real_context)
|
||||
for tenant_id in new_context["tenants"]:
|
||||
new_context["tenants"][tenant_id].setdefault("ec2_servers", [])
|
||||
for i in range(servers_per_tenant):
|
||||
new_context["tenants"][tenant_id]["ec2_servers"].append(str(i))
|
||||
|
||||
servers_ctx = servers.EC2ServerGenerator(real_context)
|
||||
servers_ctx.setup()
|
||||
self.assertEqual(new_context, servers_ctx.context)
|
||||
|
||||
@mock.patch("%s.servers.osclients" % CTX)
|
||||
@mock.patch("%s.servers.resource_manager.cleanup" % CTX)
|
||||
def test_cleanup(self, mock_cleanup, mock_osclients):
|
||||
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
servers_per_tenant = 5
|
||||
|
||||
tenants, users = self._gen_tenants_and_users(tenants_count,
|
||||
users_per_tenant)
|
||||
for tenant_id in tenants.keys():
|
||||
tenants[tenant_id].setdefault("ec2_servers", [])
|
||||
for i in range(servers_per_tenant):
|
||||
tenants[tenant_id]["ec2_servers"].append(str(i))
|
||||
|
||||
context = self._get_context(users, tenants)
|
||||
|
||||
servers_ctx = servers.EC2ServerGenerator(context)
|
||||
servers_ctx.cleanup()
|
||||
|
||||
mock_cleanup.assert_called_once_with(names=["ec2.servers"],
|
||||
users=context["users"])
|
@ -22,7 +22,7 @@ class EC2ServersTestCase(test.ScenarioTestCase):
|
||||
|
||||
def test_boot_server(self):
|
||||
scenario = servers.EC2Servers()
|
||||
scenario._boot_server = mock.Mock()
|
||||
scenario._boot_servers = mock.Mock()
|
||||
scenario.boot_server("foo_image", "foo_flavor", foo="bar")
|
||||
scenario._boot_server.assert_called_once_with(
|
||||
scenario._boot_servers.assert_called_once_with(
|
||||
"foo_image", "foo_flavor", foo="bar")
|
||||
|
@ -18,7 +18,6 @@ from oslo_config import cfg
|
||||
from rally.plugins.openstack.scenarios.ec2 import utils
|
||||
from tests.unit import test
|
||||
|
||||
EC2_UTILS = "rally.plugins.openstack.scenarios.ec2.utils"
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@ -26,27 +25,39 @@ class EC2ScenarioTestCase(test.ScenarioTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(EC2ScenarioTestCase, self).setUp()
|
||||
self.server = mock.MagicMock()
|
||||
self.reservation = mock.MagicMock(instances=[self.server])
|
||||
|
||||
def test__boot_server(self):
|
||||
self.clients("ec2").run_instances.return_value = self.reservation
|
||||
ec2_scenario = utils.EC2Scenario(context={})
|
||||
ec2_scenario._update_resource = mock.Mock()
|
||||
return_server = ec2_scenario._boot_server("image", "flavor")
|
||||
self.mock_wait_for.mock.assert_called_once_with(
|
||||
self.server,
|
||||
is_ready=self.mock_resource_is.mock.return_value,
|
||||
update_resource=ec2_scenario._update_resource,
|
||||
check_interval=CONF.benchmark.ec2_server_boot_poll_interval,
|
||||
timeout=CONF.benchmark.ec2_server_boot_timeout)
|
||||
self.mock_resource_is.mock.assert_called_once_with("RUNNING")
|
||||
self.assertEqual(self.mock_wait_for.mock.return_value, return_server)
|
||||
self._test_atomic_action_timer(ec2_scenario.atomic_actions(),
|
||||
"ec2.boot_server")
|
||||
self.server1 = mock.MagicMock()
|
||||
self.server2 = mock.MagicMock()
|
||||
self.reservations = mock.MagicMock(instances=[self.server1,
|
||||
self.server2])
|
||||
|
||||
def test__update_resource(self):
|
||||
resource = mock.MagicMock()
|
||||
scenario = utils.EC2Scenario()
|
||||
self.assertEqual(scenario._update_resource(resource), resource)
|
||||
resource.update.assert_called_once_with()
|
||||
|
||||
def test__boot_servers(self):
|
||||
self.clients("ec2").run_instances.return_value = self.reservations
|
||||
ec2_scenario = utils.EC2Scenario(context={})
|
||||
ec2_scenario._update_resource = mock.Mock()
|
||||
ec2_scenario._boot_servers("image", "flavor", 2)
|
||||
expected = [
|
||||
mock.call(
|
||||
self.server1,
|
||||
is_ready=self.mock_resource_is.mock.return_value,
|
||||
update_resource=ec2_scenario._update_resource,
|
||||
check_interval=CONF.benchmark.ec2_server_boot_poll_interval,
|
||||
timeout=CONF.benchmark.ec2_server_boot_timeout
|
||||
),
|
||||
mock.call(
|
||||
self.server2,
|
||||
is_ready=self.mock_resource_is.mock.return_value,
|
||||
update_resource=ec2_scenario._update_resource,
|
||||
check_interval=CONF.benchmark.ec2_server_boot_poll_interval,
|
||||
timeout=CONF.benchmark.ec2_server_boot_timeout
|
||||
)
|
||||
]
|
||||
self.mock_wait_for.mock.assert_has_calls(expected)
|
||||
self.mock_resource_is.mock.assert_has_calls([mock.call("RUNNING")])
|
||||
self._test_atomic_action_timer(ec2_scenario.atomic_actions(),
|
||||
"ec2.boot_servers")
|
||||
|
Loading…
x
Reference in New Issue
Block a user